1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (c) 2002-2023 Zuse Institute Berlin (ZIB) */
7 /* */
8 /* Licensed under the Apache License, Version 2.0 (the "License"); */
9 /* you may not use this file except in compliance with the License. */
10 /* You may obtain a copy of the License at */
11 /* */
12 /* http://www.apache.org/licenses/LICENSE-2.0 */
13 /* */
14 /* Unless required by applicable law or agreed to in writing, software */
15 /* distributed under the License is distributed on an "AS IS" BASIS, */
16 /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17 /* See the License for the specific language governing permissions and */
18 /* limitations under the License. */
19 /* */
20 /* You should have received a copy of the Apache-2.0 license */
21 /* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
22 /* */
23 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25 /**@file var.c
26 * @ingroup OTHER_CFILES
27 * @brief methods for problem variables
28 * @author Tobias Achterberg
29 * @author Timo Berthold
30 * @author Gerald Gamrath
31 * @author Stefan Heinz
32 * @author Marc Pfetsch
33 * @author Michael Winkler
34 * @author Kati Wolter
35 * @author Stefan Vigerske
36 *
37 * @todo Possibly implement the access of bounds of multi-aggregated variables by accessing the
38 * corresponding linear constraint if it exists. This seems to require some work, since the linear
39 * constraint has to be stored. Moreover, it has even to be created in case the original constraint
40 * was deleted after multi-aggregation, but the bounds of the multi-aggregated variable should be
41 * changed. This has to be done with care in order to not loose the performance gains of
42 * multi-aggregation.
43 */
44
45 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
46
47 #include "scip/cons.h"
48 #include "scip/event.h"
49 #include "scip/history.h"
50 #include "scip/implics.h"
51 #include "scip/lp.h"
52 #include "scip/primal.h"
53 #include "scip/prob.h"
54 #include "scip/pub_cons.h"
55 #include "scip/pub_history.h"
56 #include "scip/pub_implics.h"
57 #include "scip/pub_lp.h"
58 #include "scip/pub_message.h"
59 #include "scip/pub_misc.h"
60 #include "scip/pub_misc_sort.h"
61 #include "scip/pub_prop.h"
62 #include "scip/pub_var.h"
63 #include "scip/relax.h"
64 #include "scip/set.h"
65 #include "scip/sol.h"
66 #include "scip/stat.h"
67 #include "scip/struct_event.h"
68 #include "scip/struct_lp.h"
69 #include "scip/struct_prob.h"
70 #include "scip/struct_set.h"
71 #include "scip/struct_stat.h"
72 #include "scip/struct_var.h"
73 #include "scip/tree.h"
74 #include "scip/var.h"
75 #include <string.h>
76
77 #define MAXIMPLSCLOSURE 100 /**< maximal number of descendants of implied variable for building closure
78 * in implication graph */
79 #define MAXABSVBCOEF 1e+5 /**< maximal absolute coefficient in variable bounds added due to implications */
80
81
82 /*
83 * Debugging variable release and capture
84 *
85 * Define DEBUGUSES_VARNAME to the name of the variable for which to print
86 * a backtrace when it is captured and released.
87 * Optionally define DEBUGUSES_PROBNAME to the name of a SCIP problem to consider.
88 * Have DEBUGUSES_NOADDR2LINE defined if you do not have addr2line installed on your system.
89 */
90 /* #define DEBUGUSES_VARNAME "t_t_b7" */
91 /* #define DEBUGUSES_PROBNAME "t_st_e35_rens" */
92 /* #define DEBUGUSES_NOADDR2LINE */
93
94 #ifdef DEBUGUSES_VARNAME
95 #include <execinfo.h>
96 #include <stdio.h>
97 #include <stdlib.h>
98 #include "scip/struct_scip.h"
99
100 /** obtains a backtrace and prints it to stdout. */
101 static
102 void print_backtrace(void)
103 {
104 void* array[10];
105 char** strings;
106 int size;
107 int i;
108
109 size = backtrace(array, 10);
110 strings = backtrace_symbols(array, size);
111 if( strings == NULL )
112 return;
113
114 /* skip first entry, which is the print_backtrace function */
115 for( i = 1; i < size; ++i )
116 {
117 /* if string is something like
118 * /path/to/scip/bin/../lib/shared/libscip-7.0.1.3.linux.x86_64.gnu.dbg.so(+0x2675dd3)
119 * (that is, no function name because it is a inlined function), then call
120 * addr2line -e <libname> <addr> to get func and code line
121 * dladdr() may be an alternative
122 */
123 char* openpar;
124 char* closepar = NULL;
125 #ifndef DEBUGUSES_NOADDR2LINE
126 openpar = strchr(strings[i], '(');
127 if( openpar != NULL && openpar[1] == '+' )
128 closepar = strchr(openpar+2, ')');
129 #endif
130 if( closepar != NULL )
131 {
132 char cmd[SCIP_MAXSTRLEN];
133 (void) SCIPsnprintf(cmd, SCIP_MAXSTRLEN, "addr2line -f -p -e \"%.*s\" %.*s", openpar - strings[i], strings[i], closepar-openpar-1, openpar+1);
134 printf(" ");
135 fflush(stdout);
136 system(cmd);
137 }
138 else
139 printf(" %s\n", strings[i]);
140 }
141
142 free(strings);
143 }
144 #endif
145
146 /*
147 * hole, holelist, and domain methods
148 */
149
150 /** creates a new holelist element */
151 static
152 SCIP_RETCODE holelistCreate(
153 SCIP_HOLELIST** holelist, /**< pointer to holelist to create */
154 BMS_BLKMEM* blkmem, /**< block memory for target holelist */
155 SCIP_SET* set, /**< global SCIP settings */
156 SCIP_Real left, /**< left bound of open interval in new hole */
157 SCIP_Real right /**< right bound of open interval in new hole */
158 )
159 {
160 assert(holelist != NULL);
161 assert(blkmem != NULL);
162 assert(SCIPsetIsLT(set, left, right));
163
164 SCIPsetDebugMsg(set, "create hole list element (%.15g,%.15g) in blkmem %p\n", left, right, (void*)blkmem);
165
166 SCIP_ALLOC( BMSallocBlockMemory(blkmem, holelist) );
167 (*holelist)->hole.left = left;
168 (*holelist)->hole.right = right;
169 (*holelist)->next = NULL;
170
171 return SCIP_OKAY;
172 }
173
174 /** frees all elements in the holelist */
175 static
176 void holelistFree(
177 SCIP_HOLELIST** holelist, /**< pointer to holelist to free */
178 BMS_BLKMEM* blkmem /**< block memory for target holelist */
179 )
180 {
181 assert(holelist != NULL);
182 assert(blkmem != NULL);
183
184 while( *holelist != NULL )
185 {
186 SCIP_HOLELIST* next;
187
188 SCIPdebugMessage("free hole list element (%.15g,%.15g) in blkmem %p\n",
189 (*holelist)->hole.left, (*holelist)->hole.right, (void*)blkmem);
190
191 next = (*holelist)->next;
192 BMSfreeBlockMemory(blkmem, holelist);
193 assert(*holelist == NULL);
194
195 *holelist = next;
196 }
197 assert(*holelist == NULL);
198 }
199
200 /** duplicates a list of holes */
201 static
202 SCIP_RETCODE holelistDuplicate(
203 SCIP_HOLELIST** target, /**< pointer to target holelist */
204 BMS_BLKMEM* blkmem, /**< block memory for target holelist */
205 SCIP_SET* set, /**< global SCIP settings */
206 SCIP_HOLELIST* source /**< holelist to duplicate */
207 )
208 {
209 assert(target != NULL);
210
211 while( source != NULL )
212 {
213 assert(source->next == NULL || SCIPsetIsGE(set, source->next->hole.left, source->hole.right));
214 SCIP_CALL( holelistCreate(target, blkmem, set, source->hole.left, source->hole.right) );
215 source = source->next;
216 target = &(*target)->next;
217 }
218
219 return SCIP_OKAY;
220 }
221
222 /** adds a hole to the domain */
223 static
224 SCIP_RETCODE domAddHole(
225 SCIP_DOM* dom, /**< domain to add hole to */
226 BMS_BLKMEM* blkmem, /**< block memory */
227 SCIP_SET* set, /**< global SCIP settings */
228 SCIP_Real left, /**< left bound of open interval in new hole */
229 SCIP_Real right, /**< right bound of open interval in new hole */
230 SCIP_Bool* added /**< pointer to store whether the hole was added (variable didn't had that hole before), or NULL */
231 )
232 {
233 SCIP_HOLELIST** insertpos;
234 SCIP_HOLELIST* next;
235
236 assert(dom != NULL);
237 assert(added != NULL);
238
239 /* search for the position of the new hole */
240 insertpos = &dom->holelist;
241 while( *insertpos != NULL && (*insertpos)->hole.left < left )
242 insertpos = &(*insertpos)->next;
243
244 /* check if new hole already exists in the hole list or is a sub hole of an existing one */
245 if( *insertpos != NULL && (*insertpos)->hole.left == left && (*insertpos)->hole.right >= right ) /*lint !e777 */
246 {
247 SCIPsetDebugMsg(set, "new hole (%.15g,%.15g) is redundant through known hole (%.15g,%.15g)\n",
248 left, right, (*insertpos)->hole.left, (*insertpos)->hole.right);
249 *added = FALSE;
250 return SCIP_OKAY;
251 }
252
253 /* add hole */
254 *added = TRUE;
255
256 next = *insertpos;
257 SCIP_CALL( holelistCreate(insertpos, blkmem, set, left, right) );
258 (*insertpos)->next = next;
259
260 return SCIP_OKAY;
261 }
262
263 /** merges overlapping holes into single holes, computes and moves lower and upper bound, respectively */
264 /**@todo the domMerge() method is currently called if a lower or an upper bound locally or globally changed; this could
265 * be more efficient if performed with the knowledge if it was a lower or an upper bound which triggered this
266 * merge */
267 static
268 void domMerge(
269 SCIP_DOM* dom, /**< domain to merge */
270 BMS_BLKMEM* blkmem, /**< block memory */
271 SCIP_SET* set, /**< global SCIP settings */
272 SCIP_Real* newlb, /**< pointer to store new lower bound */
273 SCIP_Real* newub /**< pointer to store new upper bound */
274 )
275 {
276 SCIP_HOLELIST** holelistptr;
277 SCIP_HOLELIST** lastnextptr;
278 SCIP_Real* lastrightptr;
279
280 assert(dom != NULL);
281 assert(SCIPsetIsLE(set, dom->lb, dom->ub));
282
283 #ifndef NDEBUG
284 {
285 /* check if the holelist is sorted w.r.t. to the left interval bounds */
286 SCIP_Real lastleft;
287
288 holelistptr = &dom->holelist;
289
290 lastleft = -SCIPsetInfinity(set);
291
292 while( *holelistptr != NULL )
293 {
294 if( (*holelistptr)->next != NULL )
295 {
296 assert( SCIPsetIsLE(set, lastleft, (*holelistptr)->hole.left) );
297 lastleft = (*holelistptr)->hole.left;
298 }
299
300 holelistptr = &(*holelistptr)->next;
301 }
302 }
303 #endif
304
305 SCIPsetDebugMsg(set, "merge hole list\n");
306
307 holelistptr = &dom->holelist;
308 lastrightptr = &dom->lb; /* lower bound is the right bound of the hole (-infinity,lb) */
309 lastnextptr = holelistptr;
310
311 while( *holelistptr != NULL )
312 {
313 SCIPsetDebugMsg(set, "check hole (%.15g,%.15g) last right interval was <%.15g>\n", (*holelistptr)->hole.left, (*holelistptr)->hole.right, *lastrightptr);
314
315 /* check that the hole is not empty */
316 assert(SCIPsetIsLT(set, (*holelistptr)->hole.left, (*holelistptr)->hole.right));
317
318 if( SCIPsetIsGE(set, (*holelistptr)->hole.left, dom->ub) )
319 {
320 /* the remaining holes start behind the upper bound: remove them */
321 SCIPsetDebugMsg(set, "remove remaining hole since upper bound <%.15g> is less then the left hand side of the current hole\n", dom->ub);
322 holelistFree(holelistptr, blkmem);
323 assert(*holelistptr == NULL);
324
325 /* unlink this hole from the previous hole */
326 *lastnextptr = NULL;
327 }
328 else if( SCIPsetIsGT(set, (*holelistptr)->hole.right, dom->ub) )
329 {
330 /* the hole overlaps the upper bound: decrease upper bound, remove this hole and all remaining holes */
331 SCIPsetDebugMsg(set, "upper bound <%.15g> lays in current hole; store new upper bound and remove this and all remaining holes\n", dom->ub);
332
333 assert(SCIPsetIsLT(set, (*holelistptr)->hole.left, dom->ub));
334
335 /* adjust upper bound */
336 dom->ub = (*holelistptr)->hole.left;
337
338 if(newub != NULL )
339 *newub = (*holelistptr)->hole.left;
340
341 /* remove remaining hole list */
342 holelistFree(holelistptr, blkmem);
343 assert(*holelistptr == NULL);
344
345 /* unlink this hole from the previous hole */
346 *lastnextptr = NULL;
347 }
348 else if( SCIPsetIsGT(set, *lastrightptr, (*holelistptr)->hole.left) )
349 {
350 /* the right bound of the last hole is greater than the left bound of this hole: increase the right bound of
351 * the last hole, delete this hole */
352 SCIP_HOLELIST* nextholelist;
353
354 if( SCIPsetIsEQ(set, *lastrightptr, dom->lb ) )
355 {
356 /* the reason for the overlap results from the lower bound hole (-infinity,lb); therefore, we can increase
357 * the lower bound */
358 SCIPsetDebugMsg(set, "lower bound <%.15g> lays in current hole; store new lower bound and remove hole\n", dom->lb);
359 *lastrightptr = MAX(*lastrightptr, (*holelistptr)->hole.right);
360
361 /* adjust lower bound */
362 dom->lb = *lastrightptr;
363
364 if(newlb != NULL )
365 *newlb = *lastrightptr;
366 }
367 else
368 {
369 SCIPsetDebugMsg(set, "current hole overlaps with the previous one (...,%.15g); merge to (...,%.15g)\n",
370 *lastrightptr, MAX(*lastrightptr, (*holelistptr)->hole.right) );
371 *lastrightptr = MAX(*lastrightptr, (*holelistptr)->hole.right);
372 }
373 nextholelist = (*holelistptr)->next;
374 (*holelistptr)->next = NULL;
375 holelistFree(holelistptr, blkmem);
376
377 /* connect the linked list after removing the hole */
378 *lastnextptr = nextholelist;
379
380 /* get next hole */
381 *holelistptr = nextholelist;
382 }
383 else
384 {
385 /* the holes do not overlap: update lastholelist and lastrightptr */
386 lastrightptr = &(*holelistptr)->hole.right;
387 lastnextptr = &(*holelistptr)->next;
388
389 /* get next hole */
390 holelistptr = &(*holelistptr)->next;
391 }
392 }
393
394 #ifndef NDEBUG
395 {
396 /* check that holes are merged */
397 SCIP_Real lastright;
398
399 lastright = dom->lb; /* lower bound is the right bound of the hole (-infinity,lb) */
400 holelistptr = &dom->holelist;
401
402 while( *holelistptr != NULL )
403 {
404 /* check the the last right interval is smaller or equal to the current left interval (none overlapping) */
405 assert( SCIPsetIsLE(set, lastright, (*holelistptr)->hole.left) );
406
407 /* check the hole property (check that the hole is not empty) */
408 assert( SCIPsetIsLT(set, (*holelistptr)->hole.left, (*holelistptr)->hole.right) );
409 lastright = (*holelistptr)->hole.right;
410
411 /* get next hole */
412 holelistptr = &(*holelistptr)->next;
413 }
414
415 /* check the the last right interval is smaller or equal to the upper bound (none overlapping) */
416 assert( SCIPsetIsLE(set, lastright, dom->ub) );
417 }
418 #endif
419 }
420
421 /*
422 * domain change methods
423 */
424
425 /** ensures, that bound change info array for lower bound changes can store at least num entries */
426 static
427 SCIP_RETCODE varEnsureLbchginfosSize(
428 SCIP_VAR* var, /**< problem variable */
429 BMS_BLKMEM* blkmem, /**< block memory */
430 SCIP_SET* set, /**< global SCIP settings */
431 int num /**< minimum number of entries to store */
432 )
433 {
434 assert(var != NULL);
435 assert(var->nlbchginfos <= var->lbchginfossize);
436 assert(SCIPvarIsTransformed(var));
437
438 if( num > var->lbchginfossize )
439 {
440 int newsize;
441
442 newsize = SCIPsetCalcMemGrowSize(set, num);
443 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &var->lbchginfos, var->lbchginfossize, newsize) );
444 var->lbchginfossize = newsize;
445 }
446 assert(num <= var->lbchginfossize);
447
448 return SCIP_OKAY;
449 }
450
451 /** ensures, that bound change info array for upper bound changes can store at least num entries */
452 static
453 SCIP_RETCODE varEnsureUbchginfosSize(
454 SCIP_VAR* var, /**< problem variable */
455 BMS_BLKMEM* blkmem, /**< block memory */
456 SCIP_SET* set, /**< global SCIP settings */
457 int num /**< minimum number of entries to store */
458 )
459 {
460 assert(var != NULL);
461 assert(var->nubchginfos <= var->ubchginfossize);
462 assert(SCIPvarIsTransformed(var));
463
464 if( num > var->ubchginfossize )
465 {
466 int newsize;
467
468 newsize = SCIPsetCalcMemGrowSize(set, num);
469 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &var->ubchginfos, var->ubchginfossize, newsize) );
470 var->ubchginfossize = newsize;
471 }
472 assert(num <= var->ubchginfossize);
473
474 return SCIP_OKAY;
475 }
476
477 /** adds domain change info to the variable's lower bound change info array */
478 static
479 SCIP_RETCODE varAddLbchginfo(
480 SCIP_VAR* var, /**< problem variable */
481 BMS_BLKMEM* blkmem, /**< block memory */
482 SCIP_SET* set, /**< global SCIP settings */
483 SCIP_Real oldbound, /**< old value for bound */
484 SCIP_Real newbound, /**< new value for bound */
485 int depth, /**< depth in the tree, where the bound change takes place */
486 int pos, /**< position of the bound change in its bound change array */
487 SCIP_VAR* infervar, /**< variable that was changed (parent of var, or var itself) */
488 SCIP_CONS* infercons, /**< constraint that inferred this bound change, or NULL */
489 SCIP_PROP* inferprop, /**< propagator that deduced the bound change, or NULL */
490 int inferinfo, /**< user information for inference to help resolving the conflict */
491 SCIP_BOUNDTYPE inferboundtype, /**< type of bound for inference var: lower or upper bound */
492 SCIP_BOUNDCHGTYPE boundchgtype /**< bound change type: branching decision or inferred bound change */
493 )
494 {
495 assert(var != NULL);
496 assert(SCIPsetIsLT(set, oldbound, newbound));
497 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, oldbound));
498 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
499 assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, oldbound, 0.0));
500 assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, newbound, 1.0));
501 assert(boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING || infervar != NULL);
502 assert((boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER) == (infercons != NULL));
503 assert(boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER || inferprop == NULL);
504
505 SCIPsetDebugMsg(set, "adding lower bound change info to var <%s>[%g,%g]: depth=%d, pos=%d, infer%s=<%s>, inferinfo=%d, %g -> %g\n",
506 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, depth, pos, infercons != NULL ? "cons" : "prop",
507 infercons != NULL ? SCIPconsGetName(infercons) : (inferprop != NULL ? SCIPpropGetName(inferprop) : "-"), inferinfo,
508 oldbound, newbound);
509
510 SCIP_CALL( varEnsureLbchginfosSize(var, blkmem, set, var->nlbchginfos+1) );
511 var->lbchginfos[var->nlbchginfos].oldbound = oldbound;
512 var->lbchginfos[var->nlbchginfos].newbound = newbound;
513 var->lbchginfos[var->nlbchginfos].var = var;
514 var->lbchginfos[var->nlbchginfos].bdchgidx.depth = depth;
515 var->lbchginfos[var->nlbchginfos].bdchgidx.pos = pos;
516 var->lbchginfos[var->nlbchginfos].pos = var->nlbchginfos; /*lint !e732*/
517 var->lbchginfos[var->nlbchginfos].boundchgtype = boundchgtype; /*lint !e641*/
518 var->lbchginfos[var->nlbchginfos].boundtype = SCIP_BOUNDTYPE_LOWER; /*lint !e641*/
519 var->lbchginfos[var->nlbchginfos].redundant = FALSE;
520 var->lbchginfos[var->nlbchginfos].inferboundtype = inferboundtype; /*lint !e641*/
521 var->lbchginfos[var->nlbchginfos].inferencedata.var = infervar;
522 var->lbchginfos[var->nlbchginfos].inferencedata.info = inferinfo;
523
524 /**@note The "pos" data member of the bound change info has a size of 27 bits */
525 assert(var->nlbchginfos < 1 << 27);
526
527 switch( boundchgtype )
528 {
529 case SCIP_BOUNDCHGTYPE_BRANCHING:
530 break;
531 case SCIP_BOUNDCHGTYPE_CONSINFER:
532 assert(infercons != NULL);
533 var->lbchginfos[var->nlbchginfos].inferencedata.reason.cons = infercons;
534 break;
535 case SCIP_BOUNDCHGTYPE_PROPINFER:
536 var->lbchginfos[var->nlbchginfos].inferencedata.reason.prop = inferprop;
537 break;
538 default:
539 SCIPerrorMessage("invalid bound change type %d\n", boundchgtype);
540 return SCIP_INVALIDDATA;
541 }
542
543 var->nlbchginfos++;
544
545 assert(var->nlbchginfos < 2
546 || SCIPbdchgidxIsEarlier(&var->lbchginfos[var->nlbchginfos-2].bdchgidx,
547 &var->lbchginfos[var->nlbchginfos-1].bdchgidx));
548
549 return SCIP_OKAY;
550 }
551
552 /** adds domain change info to the variable's upper bound change info array */
553 static
554 SCIP_RETCODE varAddUbchginfo(
555 SCIP_VAR* var, /**< problem variable */
556 BMS_BLKMEM* blkmem, /**< block memory */
557 SCIP_SET* set, /**< global SCIP settings */
558 SCIP_Real oldbound, /**< old value for bound */
559 SCIP_Real newbound, /**< new value for bound */
560 int depth, /**< depth in the tree, where the bound change takes place */
561 int pos, /**< position of the bound change in its bound change array */
562 SCIP_VAR* infervar, /**< variable that was changed (parent of var, or var itself) */
563 SCIP_CONS* infercons, /**< constraint that inferred this bound change, or NULL */
564 SCIP_PROP* inferprop, /**< propagator that deduced the bound change, or NULL */
565 int inferinfo, /**< user information for inference to help resolving the conflict */
566 SCIP_BOUNDTYPE inferboundtype, /**< type of bound for inference var: lower or upper bound */
567 SCIP_BOUNDCHGTYPE boundchgtype /**< bound change type: branching decision or inferred bound change */
568 )
569 {
570 assert(var != NULL);
571 assert(SCIPsetIsGT(set, oldbound, newbound));
572 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, oldbound));
573 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
574 assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, oldbound, 1.0));
575 assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, newbound, 0.0));
576 assert(boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING || infervar != NULL);
577 assert((boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER) == (infercons != NULL));
578 assert(boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER || inferprop == NULL);
579
580 SCIPsetDebugMsg(set, "adding upper bound change info to var <%s>[%g,%g]: depth=%d, pos=%d, infer%s=<%s>, inferinfo=%d, %g -> %g\n",
581 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, depth, pos, infercons != NULL ? "cons" : "prop",
582 infercons != NULL ? SCIPconsGetName(infercons) : (inferprop != NULL ? SCIPpropGetName(inferprop) : "-"), inferinfo,
583 oldbound, newbound);
584
585 SCIP_CALL( varEnsureUbchginfosSize(var, blkmem, set, var->nubchginfos+1) );
586 var->ubchginfos[var->nubchginfos].oldbound = oldbound;
587 var->ubchginfos[var->nubchginfos].newbound = newbound;
588 var->ubchginfos[var->nubchginfos].var = var;
589 var->ubchginfos[var->nubchginfos].bdchgidx.depth = depth;
590 var->ubchginfos[var->nubchginfos].bdchgidx.pos = pos;
591 var->ubchginfos[var->nubchginfos].pos = var->nubchginfos; /*lint !e732*/
592 var->ubchginfos[var->nubchginfos].boundchgtype = boundchgtype; /*lint !e641*/
593 var->ubchginfos[var->nubchginfos].boundtype = SCIP_BOUNDTYPE_UPPER; /*lint !e641*/
594 var->ubchginfos[var->nubchginfos].redundant = FALSE;
595 var->ubchginfos[var->nubchginfos].inferboundtype = inferboundtype; /*lint !e641*/
596 var->ubchginfos[var->nubchginfos].inferencedata.var = infervar;
597 var->ubchginfos[var->nubchginfos].inferencedata.info = inferinfo;
598
599 /**@note The "pos" data member of the bound change info has a size of 27 bits */
600 assert(var->nubchginfos < 1 << 27);
601
602 switch( boundchgtype )
603 {
604 case SCIP_BOUNDCHGTYPE_BRANCHING:
605 break;
606 case SCIP_BOUNDCHGTYPE_CONSINFER:
607 assert(infercons != NULL);
608 var->ubchginfos[var->nubchginfos].inferencedata.reason.cons = infercons;
609 break;
610 case SCIP_BOUNDCHGTYPE_PROPINFER:
611 var->ubchginfos[var->nubchginfos].inferencedata.reason.prop = inferprop;
612 break;
613 default:
614 SCIPerrorMessage("invalid bound change type %d\n", boundchgtype);
615 return SCIP_INVALIDDATA;
616 }
617
618 var->nubchginfos++;
619
620 assert(var->nubchginfos < 2
621 || SCIPbdchgidxIsEarlier(&var->ubchginfos[var->nubchginfos-2].bdchgidx,
622 &var->ubchginfos[var->nubchginfos-1].bdchgidx));
623
624 return SCIP_OKAY;
625 }
626
627 /** applies single bound change */
628 SCIP_RETCODE SCIPboundchgApply(
629 SCIP_BOUNDCHG* boundchg, /**< bound change to apply */
630 BMS_BLKMEM* blkmem, /**< block memory */
631 SCIP_SET* set, /**< global SCIP settings */
632 SCIP_STAT* stat, /**< problem statistics */
633 SCIP_LP* lp, /**< current LP data */
634 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
635 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
636 int depth, /**< depth in the tree, where the bound change takes place */
637 int pos, /**< position of the bound change in its bound change array */
638 SCIP_Bool* cutoff /**< pointer to store whether an infeasible bound change was detected */
639 )
640 {
641 SCIP_VAR* var;
642
643 assert(boundchg != NULL);
644 assert(stat != NULL);
645 assert(depth > 0);
646 assert(pos >= 0);
647 assert(cutoff != NULL);
648
649 *cutoff = FALSE;
650
651 /* ignore redundant bound changes */
652 if( boundchg->redundant )
653 return SCIP_OKAY;
654
655 var = boundchg->var;
656 assert(var != NULL);
657 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
658 assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, boundchg->newbound));
659
660 /* apply bound change */
661 switch( boundchg->boundtype )
662 {
663 case SCIP_BOUNDTYPE_LOWER:
664 /* check, if the bound change is still active (could be replaced by inference due to repropagation of higher node) */
665 if( SCIPsetIsGT(set, boundchg->newbound, var->locdom.lb) )
666 {
667 if( SCIPsetIsLE(set, boundchg->newbound, var->locdom.ub) )
668 {
669 /* add the bound change info to the variable's bound change info array */
670 switch( boundchg->boundchgtype )
671 {
672 case SCIP_BOUNDCHGTYPE_BRANCHING:
673 SCIPsetDebugMsg(set, " -> branching: new lower bound of <%s>[%g,%g]: %g\n",
674 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
675 SCIP_CALL( varAddLbchginfo(var, blkmem, set, var->locdom.lb, boundchg->newbound, depth, pos,
676 NULL, NULL, NULL, 0, SCIP_BOUNDTYPE_LOWER, SCIP_BOUNDCHGTYPE_BRANCHING) );
677 stat->lastbranchvar = var;
678 stat->lastbranchdir = SCIP_BRANCHDIR_UPWARDS;
679 stat->lastbranchvalue = boundchg->newbound;
680 break;
681
682 case SCIP_BOUNDCHGTYPE_CONSINFER:
683 assert(boundchg->data.inferencedata.reason.cons != NULL);
684 SCIPsetDebugMsg(set, " -> constraint <%s> inference: new lower bound of <%s>[%g,%g]: %g\n",
685 SCIPconsGetName(boundchg->data.inferencedata.reason.cons),
686 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
687 SCIP_CALL( varAddLbchginfo(var, blkmem, set, var->locdom.lb, boundchg->newbound, depth, pos,
688 boundchg->data.inferencedata.var, boundchg->data.inferencedata.reason.cons, NULL,
689 boundchg->data.inferencedata.info,
690 (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_CONSINFER) );
691 break;
692
693 case SCIP_BOUNDCHGTYPE_PROPINFER:
694 SCIPsetDebugMsg(set, " -> propagator <%s> inference: new lower bound of <%s>[%g,%g]: %g\n",
695 boundchg->data.inferencedata.reason.prop != NULL
696 ? SCIPpropGetName(boundchg->data.inferencedata.reason.prop) : "-",
697 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
698 SCIP_CALL( varAddLbchginfo(var, blkmem, set, var->locdom.lb, boundchg->newbound, depth, pos,
699 boundchg->data.inferencedata.var, NULL, boundchg->data.inferencedata.reason.prop,
700 boundchg->data.inferencedata.info,
701 (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_PROPINFER) );
702 break;
703
704 default:
705 SCIPerrorMessage("invalid bound change type %d\n", boundchg->boundchgtype);
706 return SCIP_INVALIDDATA;
707 }
708
709 /* change local bound of variable */
710 SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, boundchg->newbound) );
711 }
712 else
713 {
714 SCIPsetDebugMsg(set, " -> cutoff: new lower bound of <%s>[%g,%g]: %g\n",
715 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
716 *cutoff = TRUE;
717 boundchg->redundant = TRUE; /* bound change has not entered the lbchginfos array of the variable! */
718 }
719 }
720 else
721 {
722 /* mark bound change to be inactive */
723 SCIPsetDebugMsg(set, " -> inactive %s: new lower bound of <%s>[%g,%g]: %g\n",
724 (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING ? "branching" : "inference",
725 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
726 boundchg->redundant = TRUE;
727 }
728 break;
729
730 case SCIP_BOUNDTYPE_UPPER:
731 /* check, if the bound change is still active (could be replaced by inference due to repropagation of higher node) */
732 if( SCIPsetIsLT(set, boundchg->newbound, var->locdom.ub) )
733 {
734 if( SCIPsetIsGE(set, boundchg->newbound, var->locdom.lb) )
735 {
736 /* add the bound change info to the variable's bound change info array */
737 switch( boundchg->boundchgtype )
738 {
739 case SCIP_BOUNDCHGTYPE_BRANCHING:
740 SCIPsetDebugMsg(set, " -> branching: new upper bound of <%s>[%g,%g]: %g\n",
741 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
742 SCIP_CALL( varAddUbchginfo(var, blkmem, set, var->locdom.ub, boundchg->newbound, depth, pos,
743 NULL, NULL, NULL, 0, SCIP_BOUNDTYPE_UPPER, SCIP_BOUNDCHGTYPE_BRANCHING) );
744 stat->lastbranchvar = var;
745 stat->lastbranchdir = SCIP_BRANCHDIR_DOWNWARDS;
746 stat->lastbranchvalue = boundchg->newbound;
747 break;
748
749 case SCIP_BOUNDCHGTYPE_CONSINFER:
750 assert(boundchg->data.inferencedata.reason.cons != NULL);
751 SCIPsetDebugMsg(set, " -> constraint <%s> inference: new upper bound of <%s>[%g,%g]: %g\n",
752 SCIPconsGetName(boundchg->data.inferencedata.reason.cons),
753 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
754 SCIP_CALL( varAddUbchginfo(var, blkmem, set, var->locdom.ub, boundchg->newbound, depth, pos,
755 boundchg->data.inferencedata.var, boundchg->data.inferencedata.reason.cons, NULL,
756 boundchg->data.inferencedata.info,
757 (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_CONSINFER) );
758 break;
759
760 case SCIP_BOUNDCHGTYPE_PROPINFER:
761 SCIPsetDebugMsg(set, " -> propagator <%s> inference: new upper bound of <%s>[%g,%g]: %g\n",
762 boundchg->data.inferencedata.reason.prop != NULL
763 ? SCIPpropGetName(boundchg->data.inferencedata.reason.prop) : "-",
764 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
765 SCIP_CALL( varAddUbchginfo(var, blkmem, set, var->locdom.ub, boundchg->newbound, depth, pos,
766 boundchg->data.inferencedata.var, NULL, boundchg->data.inferencedata.reason.prop,
767 boundchg->data.inferencedata.info,
768 (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_PROPINFER) );
769 break;
770
771 default:
772 SCIPerrorMessage("invalid bound change type %d\n", boundchg->boundchgtype);
773 return SCIP_INVALIDDATA;
774 }
775
776 /* change local bound of variable */
777 SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, boundchg->newbound) );
778 }
779 else
780 {
781 SCIPsetDebugMsg(set, " -> cutoff: new upper bound of <%s>[%g,%g]: %g\n",
782 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
783 *cutoff = TRUE;
784 boundchg->redundant = TRUE; /* bound change has not entered the ubchginfos array of the variable! */
785 }
786 }
787 else
788 {
789 /* mark bound change to be inactive */
790 SCIPsetDebugMsg(set, " -> inactive %s: new upper bound of <%s>[%g,%g]: %g\n",
791 (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING ? "branching" : "inference",
792 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
793 boundchg->redundant = TRUE;
794 }
795 break;
796
797 default:
798 SCIPerrorMessage("unknown bound type\n");
799 return SCIP_INVALIDDATA;
800 }
801
802 /* update the branching and inference history */
803 if( !boundchg->applied && !boundchg->redundant )
804 {
805 assert(var == boundchg->var);
806
807 if( (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING )
808 {
809 SCIP_CALL( SCIPvarIncNBranchings(var, blkmem, set, stat,
810 (SCIP_BOUNDTYPE)boundchg->boundtype == SCIP_BOUNDTYPE_LOWER
811 ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS, boundchg->newbound, depth) );
812 }
813 else if( stat->lastbranchvar != NULL )
814 {
815 /**@todo if last branching variable is unknown, retrieve it from the nodes' boundchg arrays */
816 SCIP_CALL( SCIPvarIncInferenceSum(stat->lastbranchvar, blkmem, set, stat, stat->lastbranchdir, stat->lastbranchvalue, 1.0) );
817 }
818 boundchg->applied = TRUE;
819 }
820
821 return SCIP_OKAY;
822 }
823
824 /** undoes single bound change */
825 SCIP_RETCODE SCIPboundchgUndo(
826 SCIP_BOUNDCHG* boundchg, /**< bound change to remove */
827 BMS_BLKMEM* blkmem, /**< block memory */
828 SCIP_SET* set, /**< global SCIP settings */
829 SCIP_STAT* stat, /**< problem statistics */
830 SCIP_LP* lp, /**< current LP data */
831 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
832 SCIP_EVENTQUEUE* eventqueue /**< event queue */
833 )
834 {
835 SCIP_VAR* var;
836
837 assert(boundchg != NULL);
838 assert(stat != NULL);
839
840 /* ignore redundant bound changes */
841 if( boundchg->redundant )
842 return SCIP_OKAY;
843
844 var = boundchg->var;
845 assert(var != NULL);
846 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
847
848 /* undo bound change: apply the previous bound change of variable */
849 switch( boundchg->boundtype )
850 {
851 case SCIP_BOUNDTYPE_LOWER:
852 var->nlbchginfos--;
853 assert(var->nlbchginfos >= 0);
854 assert(var->lbchginfos != NULL);
855 assert( SCIPsetIsFeasEQ(set, var->lbchginfos[var->nlbchginfos].newbound, var->locdom.lb) ); /*lint !e777*/
856 assert( SCIPsetIsFeasLE(set, boundchg->newbound, var->locdom.lb) ); /* current lb might be larger to intermediate global bound change */
857
858 SCIPsetDebugMsg(set, "removed lower bound change info of var <%s>[%g,%g]: depth=%d, pos=%d, %g -> %g\n",
859 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub,
860 var->lbchginfos[var->nlbchginfos].bdchgidx.depth, var->lbchginfos[var->nlbchginfos].bdchgidx.pos,
861 var->lbchginfos[var->nlbchginfos].oldbound, var->lbchginfos[var->nlbchginfos].newbound);
862
863 /* reinstall the previous local bound */
864 SCIP_CALL( SCIPvarChgLbLocal(boundchg->var, blkmem, set, stat, lp, branchcand, eventqueue,
865 var->lbchginfos[var->nlbchginfos].oldbound) );
866
867 /* in case all bound changes are removed the local bound should match the global bound */
868 assert(var->nlbchginfos > 0 || SCIPsetIsFeasEQ(set, var->locdom.lb, var->glbdom.lb));
869
870 break;
871
872 case SCIP_BOUNDTYPE_UPPER:
873 var->nubchginfos--;
874 assert(var->nubchginfos >= 0);
875 assert(var->ubchginfos != NULL);
876 assert( SCIPsetIsFeasEQ(set, var->ubchginfos[var->nubchginfos].newbound, var->locdom.ub) ); /*lint !e777*/
877 assert( SCIPsetIsFeasGE(set, boundchg->newbound, var->locdom.ub) ); /* current ub might be smaller to intermediate global bound change */
878
879 SCIPsetDebugMsg(set, "removed upper bound change info of var <%s>[%g,%g]: depth=%d, pos=%d, %g -> %g\n",
880 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub,
881 var->ubchginfos[var->nubchginfos].bdchgidx.depth, var->ubchginfos[var->nubchginfos].bdchgidx.pos,
882 var->ubchginfos[var->nubchginfos].oldbound, var->ubchginfos[var->nubchginfos].newbound);
883
884 /* reinstall the previous local bound */
885 SCIP_CALL( SCIPvarChgUbLocal(boundchg->var, blkmem, set, stat, lp, branchcand, eventqueue,
886 var->ubchginfos[var->nubchginfos].oldbound) );
887
888 /* in case all bound changes are removed the local bound should match the global bound */
889 assert(var->nubchginfos > 0 || SCIPsetIsFeasEQ(set, var->locdom.ub, var->glbdom.ub));
890
891 break;
892
893 default:
894 SCIPerrorMessage("unknown bound type\n");
895 return SCIP_INVALIDDATA;
896 }
897
898 /* update last branching variable */
899 if( (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING )
900 {
901 stat->lastbranchvar = NULL;
902 stat->lastbranchvalue = SCIP_UNKNOWN;
903 }
904
905 return SCIP_OKAY;
906 }
907
908 /** applies single bound change to the global problem by changing the global bound of the corresponding variable */
909 static
910 SCIP_RETCODE boundchgApplyGlobal(
911 SCIP_BOUNDCHG* boundchg, /**< bound change to apply */
912 BMS_BLKMEM* blkmem, /**< block memory */
913 SCIP_SET* set, /**< global SCIP settings */
914 SCIP_STAT* stat, /**< problem statistics */
915 SCIP_LP* lp, /**< current LP data */
916 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
917 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
918 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
919 SCIP_Bool* cutoff /**< pointer to store whether an infeasible bound change was detected */
920 )
921 {
922 SCIP_VAR* var;
923 SCIP_Real newbound;
924 SCIP_BOUNDTYPE boundtype;
925
926 assert(boundchg != NULL);
927 assert(cutoff != NULL);
928
929 *cutoff = FALSE;
930
931 /* ignore redundant bound changes */
932 if( boundchg->redundant )
933 return SCIP_OKAY;
934
935 var = SCIPboundchgGetVar(boundchg);
936 newbound = SCIPboundchgGetNewbound(boundchg);
937 boundtype = SCIPboundchgGetBoundtype(boundchg);
938
939 /* check if the bound change is redundant which can happen due to a (better) global bound change which was performed
940 * after that bound change was applied
941 *
942 * @note a global bound change is not captured by the redundant member of the bound change data structure
943 */
944 if( (boundtype == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasLE(set, newbound, SCIPvarGetLbGlobal(var)))
945 || (boundtype == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasGE(set, newbound, SCIPvarGetUbGlobal(var))) )
946 {
947 return SCIP_OKAY;
948 }
949
950 SCIPsetDebugMsg(set, "applying global bound change: <%s>[%g,%g] %s %g\n",
951 SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var),
952 boundtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", newbound);
953
954 /* check for cutoff */
955 if( (boundtype == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasGT(set, newbound, SCIPvarGetUbGlobal(var)))
956 || (boundtype == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasLT(set, newbound, SCIPvarGetLbGlobal(var))) )
957 {
958 *cutoff = TRUE;
959 return SCIP_OKAY;
960 }
961
962 /* apply bound change */
963 SCIP_CALL( SCIPvarChgBdGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound, boundtype) );
964
965 return SCIP_OKAY;
966 }
967
968 /** captures branching and inference data of bound change */
969 static
970 SCIP_RETCODE boundchgCaptureData(
971 SCIP_BOUNDCHG* boundchg /**< bound change to remove */
972 )
973 {
974 assert(boundchg != NULL);
975
976 /* capture variable associated with the bound change */
977 assert(boundchg->var != NULL);
978 SCIPvarCapture(boundchg->var);
979
980 switch( boundchg->boundchgtype )
981 {
982 case SCIP_BOUNDCHGTYPE_BRANCHING:
983 case SCIP_BOUNDCHGTYPE_PROPINFER:
984 break;
985
986 case SCIP_BOUNDCHGTYPE_CONSINFER:
987 assert(boundchg->data.inferencedata.var != NULL);
988 assert(boundchg->data.inferencedata.reason.cons != NULL);
989 SCIPconsCapture(boundchg->data.inferencedata.reason.cons);
990 break;
991
992 default:
993 SCIPerrorMessage("invalid bound change type\n");
994 return SCIP_INVALIDDATA;
995 }
996
997 return SCIP_OKAY;
998 }
999
1000 /** releases branching and inference data of bound change */
1001 static
1002 SCIP_RETCODE boundchgReleaseData(
1003 SCIP_BOUNDCHG* boundchg, /**< bound change to remove */
1004 BMS_BLKMEM* blkmem, /**< block memory */
1005 SCIP_SET* set, /**< global SCIP settings */
1006 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
1007 SCIP_LP* lp /**< current LP data */
1008
1009 )
1010 {
1011 assert(boundchg != NULL);
1012
1013 switch( boundchg->boundchgtype )
1014 {
1015 case SCIP_BOUNDCHGTYPE_BRANCHING:
1016 case SCIP_BOUNDCHGTYPE_PROPINFER:
1017 break;
1018
1019 case SCIP_BOUNDCHGTYPE_CONSINFER:
1020 assert(boundchg->data.inferencedata.var != NULL);
1021 assert(boundchg->data.inferencedata.reason.cons != NULL);
1022 SCIP_CALL( SCIPconsRelease(&boundchg->data.inferencedata.reason.cons, blkmem, set) );
1023 break;
1024
1025 default:
1026 SCIPerrorMessage("invalid bound change type\n");
1027 return SCIP_INVALIDDATA;
1028 }
1029
1030 /* release variable */
1031 assert(boundchg->var != NULL);
1032 SCIP_CALL( SCIPvarRelease(&boundchg->var, blkmem, set, eventqueue, lp) );
1033
1034 return SCIP_OKAY;
1035 }
1036
1037 /** creates empty domain change data with dynamic arrays */
1038 static
1039 SCIP_RETCODE domchgCreate(
1040 SCIP_DOMCHG** domchg, /**< pointer to domain change data */
1041 BMS_BLKMEM* blkmem /**< block memory */
1042 )
1043 {
1044 assert(domchg != NULL);
1045 assert(blkmem != NULL);
1046
1047 SCIP_ALLOC( BMSallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGDYN)) );
1048 (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_DYNAMIC; /*lint !e641*/
1049 (*domchg)->domchgdyn.nboundchgs = 0;
1050 (*domchg)->domchgdyn.boundchgs = NULL;
1051 (*domchg)->domchgdyn.nholechgs = 0;
1052 (*domchg)->domchgdyn.holechgs = NULL;
1053 (*domchg)->domchgdyn.boundchgssize = 0;
1054 (*domchg)->domchgdyn.holechgssize = 0;
1055
1056 return SCIP_OKAY;
1057 }
1058
1059 /** frees domain change data */
1060 SCIP_RETCODE SCIPdomchgFree(
1061 SCIP_DOMCHG** domchg, /**< pointer to domain change */
1062 BMS_BLKMEM* blkmem, /**< block memory */
1063 SCIP_SET* set, /**< global SCIP settings */
1064 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
1065 SCIP_LP* lp /**< current LP data */
1066 )
1067 {
1068 assert(domchg != NULL);
1069 assert(blkmem != NULL);
1070
1071 if( *domchg != NULL )
1072 {
1073 int i;
1074
1075 /* release variables, branching and inference data associated with the bound changes */
1076 for( i = 0; i < (int)(*domchg)->domchgbound.nboundchgs; ++i )
1077 {
1078 SCIP_CALL( boundchgReleaseData(&(*domchg)->domchgbound.boundchgs[i], blkmem, set, eventqueue, lp) );
1079 }
1080
1081 /* free memory for bound and hole changes */
1082 switch( (*domchg)->domchgdyn.domchgtype )
1083 {
1084 case SCIP_DOMCHGTYPE_BOUND:
1085 BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgbound.boundchgs, (*domchg)->domchgbound.nboundchgs);
1086 BMSfreeBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOUND));
1087 break;
1088 case SCIP_DOMCHGTYPE_BOTH:
1089 BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgboth.boundchgs, (*domchg)->domchgboth.nboundchgs);
1090 BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgboth.holechgs, (*domchg)->domchgboth.nholechgs);
1091 BMSfreeBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOTH));
1092 break;
1093 case SCIP_DOMCHGTYPE_DYNAMIC:
1094 BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgdyn.boundchgs, (*domchg)->domchgdyn.boundchgssize);
1095 BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgdyn.holechgs, (*domchg)->domchgdyn.holechgssize);
1096 BMSfreeBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGDYN));
1097 break;
1098 default:
1099 SCIPerrorMessage("invalid domain change type\n");
1100 return SCIP_INVALIDDATA;
1101 }
1102 }
1103
1104 return SCIP_OKAY;
1105 }
1106
1107 /** converts a static domain change data into a dynamic one */
1108 static
1109 SCIP_RETCODE domchgMakeDynamic(
1110 SCIP_DOMCHG** domchg, /**< pointer to domain change data */
1111 BMS_BLKMEM* blkmem /**< block memory */
1112 )
1113 {
1114 assert(domchg != NULL);
1115 assert(blkmem != NULL);
1116
1117 SCIPdebugMessage("making domain change data %p pointing to %p dynamic\n", (void*)domchg, (void*)*domchg);
1118
1119 if( *domchg == NULL )
1120 {
1121 SCIP_CALL( domchgCreate(domchg, blkmem) );
1122 }
1123 else
1124 {
1125 switch( (*domchg)->domchgdyn.domchgtype )
1126 {
1127 case SCIP_DOMCHGTYPE_BOUND:
1128 SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOUND), sizeof(SCIP_DOMCHGDYN)) );
1129 (*domchg)->domchgdyn.nholechgs = 0;
1130 (*domchg)->domchgdyn.holechgs = NULL;
1131 (*domchg)->domchgdyn.boundchgssize = (int) (*domchg)->domchgdyn.nboundchgs;
1132 (*domchg)->domchgdyn.holechgssize = 0;
1133 (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_DYNAMIC; /*lint !e641*/
1134 break;
1135 case SCIP_DOMCHGTYPE_BOTH:
1136 SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOTH), sizeof(SCIP_DOMCHGDYN)) );
1137 (*domchg)->domchgdyn.boundchgssize = (int) (*domchg)->domchgdyn.nboundchgs;
1138 (*domchg)->domchgdyn.holechgssize = (*domchg)->domchgdyn.nholechgs;
1139 (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_DYNAMIC; /*lint !e641*/
1140 break;
1141 case SCIP_DOMCHGTYPE_DYNAMIC:
1142 break;
1143 default:
1144 SCIPerrorMessage("invalid domain change type\n");
1145 return SCIP_INVALIDDATA;
1146 }
1147 }
1148 #ifndef NDEBUG
1149 {
1150 int i;
1151 for( i = 0; i < (int)(*domchg)->domchgbound.nboundchgs; ++i )
1152 assert(SCIPvarGetType((*domchg)->domchgbound.boundchgs[i].var) == SCIP_VARTYPE_CONTINUOUS
1153 || EPSISINT((*domchg)->domchgbound.boundchgs[i].newbound, 1e-06));
1154 }
1155 #endif
1156
1157 return SCIP_OKAY;
1158 }
1159
1160 /** converts a dynamic domain change data into a static one, using less memory than for a dynamic one */
1161 SCIP_RETCODE SCIPdomchgMakeStatic(
1162 SCIP_DOMCHG** domchg, /**< pointer to domain change data */
1163 BMS_BLKMEM* blkmem, /**< block memory */
1164 SCIP_SET* set, /**< global SCIP settings */
1165 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
1166 SCIP_LP* lp /**< current LP data */
1167 )
1168 {
1169 assert(domchg != NULL);
1170 assert(blkmem != NULL);
1171
1172 SCIPsetDebugMsg(set, "making domain change data %p pointing to %p static\n", (void*)domchg, (void*)*domchg);
1173
1174 if( *domchg != NULL )
1175 {
1176 switch( (*domchg)->domchgdyn.domchgtype )
1177 {
1178 case SCIP_DOMCHGTYPE_BOUND:
1179 if( (*domchg)->domchgbound.nboundchgs == 0 )
1180 {
1181 SCIP_CALL( SCIPdomchgFree(domchg, blkmem, set, eventqueue, lp) );
1182 }
1183 break;
1184 case SCIP_DOMCHGTYPE_BOTH:
1185 if( (*domchg)->domchgboth.nholechgs == 0 )
1186 {
1187 if( (*domchg)->domchgbound.nboundchgs == 0 )
1188 {
1189 SCIP_CALL( SCIPdomchgFree(domchg, blkmem, set, eventqueue, lp) );
1190 }
1191 else
1192 {
1193 SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOTH), sizeof(SCIP_DOMCHGBOUND)) );
1194 (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_BOUND; /*lint !e641*/
1195 }
1196 }
1197 break;
1198 case SCIP_DOMCHGTYPE_DYNAMIC:
1199 if( (*domchg)->domchgboth.nholechgs == 0 )
1200 {
1201 if( (*domchg)->domchgbound.nboundchgs == 0 )
1202 {
1203 SCIP_CALL( SCIPdomchgFree(domchg, blkmem, set, eventqueue, lp) );
1204 }
1205 else
1206 {
1207 /* shrink dynamic size arrays to their minimal sizes */
1208 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(*domchg)->domchgdyn.boundchgs, \
1209 (*domchg)->domchgdyn.boundchgssize, (*domchg)->domchgdyn.nboundchgs) ); /*lint !e571*/
1210 BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgdyn.holechgs, (*domchg)->domchgdyn.holechgssize);
1211
1212 /* convert into static domain change */
1213 SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGDYN), sizeof(SCIP_DOMCHGBOUND)) );
1214 (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_BOUND; /*lint !e641*/
1215 }
1216 }
1217 else
1218 {
1219 /* shrink dynamic size arrays to their minimal sizes */
1220 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(*domchg)->domchgdyn.boundchgs, \
1221 (*domchg)->domchgdyn.boundchgssize, (*domchg)->domchgdyn.nboundchgs) ); /*lint !e571*/
1222 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(*domchg)->domchgdyn.holechgs, \
1223 (*domchg)->domchgdyn.holechgssize, (*domchg)->domchgdyn.nholechgs) );
1224
1225 /* convert into static domain change */
1226 SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGDYN), sizeof(SCIP_DOMCHGBOTH)) );
1227 (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_BOTH; /*lint !e641*/
1228 }
1229 break;
1230 default:
1231 SCIPerrorMessage("invalid domain change type\n");
1232 return SCIP_INVALIDDATA;
1233 }
1234 #ifndef NDEBUG
1235 if( *domchg != NULL )
1236 {
1237 int i;
1238 for( i = 0; i < (int)(*domchg)->domchgbound.nboundchgs; ++i )
1239 assert(SCIPvarGetType((*domchg)->domchgbound.boundchgs[i].var) == SCIP_VARTYPE_CONTINUOUS
1240 || SCIPsetIsFeasIntegral(set, (*domchg)->domchgbound.boundchgs[i].newbound));
1241 }
1242 #endif
1243 }
1244
1245 return SCIP_OKAY;
1246 }
1247
1248 /** ensures, that boundchgs array can store at least num entries */
1249 static
1250 SCIP_RETCODE domchgEnsureBoundchgsSize(
1251 SCIP_DOMCHG* domchg, /**< domain change data structure */
1252 BMS_BLKMEM* blkmem, /**< block memory */
1253 SCIP_SET* set, /**< global SCIP settings */
1254 int num /**< minimum number of entries to store */
1255 )
1256 {
1257 assert(domchg != NULL);
1258 assert(domchg->domchgdyn.domchgtype == SCIP_DOMCHGTYPE_DYNAMIC); /*lint !e641*/
1259
1260 if( num > domchg->domchgdyn.boundchgssize )
1261 {
1262 int newsize;
1263
1264 newsize = SCIPsetCalcMemGrowSize(set, num);
1265 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &domchg->domchgdyn.boundchgs, domchg->domchgdyn.boundchgssize, newsize) );
1266 domchg->domchgdyn.boundchgssize = newsize;
1267 }
1268 assert(num <= domchg->domchgdyn.boundchgssize);
1269
1270 return SCIP_OKAY;
1271 }
1272
1273 /** ensures, that holechgs array can store at least num additional entries */
1274 static
1275 SCIP_RETCODE domchgEnsureHolechgsSize(
1276 SCIP_DOMCHG* domchg, /**< domain change data structure */
1277 BMS_BLKMEM* blkmem, /**< block memory */
1278 SCIP_SET* set, /**< global SCIP settings */
1279 int num /**< minimum number of additional entries to store */
1280 )
1281 {
1282 assert(domchg != NULL);
1283 assert(domchg->domchgdyn.domchgtype == SCIP_DOMCHGTYPE_DYNAMIC); /*lint !e641*/
1284
1285 if( num > domchg->domchgdyn.holechgssize )
1286 {
1287 int newsize;
1288
1289 newsize = SCIPsetCalcMemGrowSize(set, num);
1290 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &domchg->domchgdyn.holechgs, domchg->domchgdyn.holechgssize, newsize) );
1291 domchg->domchgdyn.holechgssize = newsize;
1292 }
1293 assert(num <= domchg->domchgdyn.holechgssize);
1294
1295 return SCIP_OKAY;
1296 }
1297
1298 /** applies domain change */
1299 SCIP_RETCODE SCIPdomchgApply(
1300 SCIP_DOMCHG* domchg, /**< domain change to apply */
1301 BMS_BLKMEM* blkmem, /**< block memory */
1302 SCIP_SET* set, /**< global SCIP settings */
1303 SCIP_STAT* stat, /**< problem statistics */
1304 SCIP_LP* lp, /**< current LP data */
1305 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
1306 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
1307 int depth, /**< depth in the tree, where the domain change takes place */
1308 SCIP_Bool* cutoff /**< pointer to store whether an infeasible domain change was detected */
1309 )
1310 {
1311 int i;
1312
1313 assert(cutoff != NULL);
1314
1315 *cutoff = FALSE;
1316
1317 SCIPsetDebugMsg(set, "applying domain changes at %p in depth %d\n", (void*)domchg, depth);
1318
1319 if( domchg == NULL )
1320 return SCIP_OKAY;
1321
1322 /* apply bound changes */
1323 for( i = 0; i < (int)domchg->domchgbound.nboundchgs; ++i )
1324 {
1325 SCIP_CALL( SCIPboundchgApply(&domchg->domchgbound.boundchgs[i], blkmem, set, stat, lp,
1326 branchcand, eventqueue, depth, i, cutoff) );
1327 if( *cutoff )
1328 break;
1329 }
1330 SCIPsetDebugMsg(set, " -> %u bound changes (cutoff %u)\n", domchg->domchgbound.nboundchgs, *cutoff);
1331
1332 /* mark all bound changes after a cutoff redundant */
1333 for( ; i < (int)domchg->domchgbound.nboundchgs; ++i )
1334 domchg->domchgbound.boundchgs[i].redundant = TRUE;
1335
1336 /* apply holelist changes */
1337 if( domchg->domchgdyn.domchgtype != SCIP_DOMCHGTYPE_BOUND ) /*lint !e641*/
1338 {
1339 for( i = 0; i < domchg->domchgboth.nholechgs; ++i )
1340 *(domchg->domchgboth.holechgs[i].ptr) = domchg->domchgboth.holechgs[i].newlist;
1341 SCIPsetDebugMsg(set, " -> %d hole changes\n", domchg->domchgboth.nholechgs);
1342 }
1343
1344 return SCIP_OKAY;
1345 }
1346
1347 /** undoes domain change */
1348 SCIP_RETCODE SCIPdomchgUndo(
1349 SCIP_DOMCHG* domchg, /**< domain change to remove */
1350 BMS_BLKMEM* blkmem, /**< block memory */
1351 SCIP_SET* set, /**< global SCIP settings */
1352 SCIP_STAT* stat, /**< problem statistics */
1353 SCIP_LP* lp, /**< current LP data */
1354 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
1355 SCIP_EVENTQUEUE* eventqueue /**< event queue */
1356 )
1357 {
1358 int i;
1359
1360 SCIPsetDebugMsg(set, "undoing domain changes at %p\n", (void*)domchg);
1361 if( domchg == NULL )
1362 return SCIP_OKAY;
1363
1364 /* undo holelist changes */
1365 if( domchg->domchgdyn.domchgtype != SCIP_DOMCHGTYPE_BOUND ) /*lint !e641*/
1366 {
1367 for( i = domchg->domchgboth.nholechgs-1; i >= 0; --i )
1368 *(domchg->domchgboth.holechgs[i].ptr) = domchg->domchgboth.holechgs[i].oldlist;
1369 SCIPsetDebugMsg(set, " -> %d hole changes\n", domchg->domchgboth.nholechgs);
1370 }
1371
1372 /* undo bound changes */
1373 for( i = domchg->domchgbound.nboundchgs-1; i >= 0; --i )
1374 {
1375 SCIP_CALL( SCIPboundchgUndo(&domchg->domchgbound.boundchgs[i], blkmem, set, stat, lp, branchcand, eventqueue) );
1376 }
1377 SCIPsetDebugMsg(set, " -> %u bound changes\n", domchg->domchgbound.nboundchgs);
1378
1379 return SCIP_OKAY;
1380 }
1381
1382 /** applies domain change to the global problem */
1383 SCIP_RETCODE SCIPdomchgApplyGlobal(
1384 SCIP_DOMCHG* domchg, /**< domain change to apply */
1385 BMS_BLKMEM* blkmem, /**< block memory */
1386 SCIP_SET* set, /**< global SCIP settings */
1387 SCIP_STAT* stat, /**< problem statistics */
1388 SCIP_LP* lp, /**< current LP data */
1389 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
1390 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
1391 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
1392 SCIP_Bool* cutoff /**< pointer to store whether an infeasible domain change was detected */
1393 )
1394 {
1395 int i;
1396
1397 assert(cutoff != NULL);
1398
1399 *cutoff = FALSE;
1400
1401 if( domchg == NULL )
1402 return SCIP_OKAY;
1403
1404 SCIPsetDebugMsg(set, "applying domain changes at %p to the global problem\n", (void*)domchg);
1405
1406 /* apply bound changes */
1407 for( i = 0; i < (int)domchg->domchgbound.nboundchgs; ++i )
1408 {
1409 SCIP_CALL( boundchgApplyGlobal(&domchg->domchgbound.boundchgs[i], blkmem, set, stat, lp,
1410 branchcand, eventqueue, cliquetable, cutoff) );
1411 if( *cutoff )
1412 break;
1413 }
1414 SCIPsetDebugMsg(set, " -> %u global bound changes\n", domchg->domchgbound.nboundchgs);
1415
1416 /**@todo globally apply holelist changes - how can this be done without confusing pointer updates? */
1417
1418 return SCIP_OKAY;
1419 }
1420
1421 /** adds bound change to domain changes */
1422 SCIP_RETCODE SCIPdomchgAddBoundchg(
1423 SCIP_DOMCHG** domchg, /**< pointer to domain change data structure */
1424 BMS_BLKMEM* blkmem, /**< block memory */
1425 SCIP_SET* set, /**< global SCIP settings */
1426 SCIP_VAR* var, /**< variable to change the bounds for */
1427 SCIP_Real newbound, /**< new value for bound */
1428 SCIP_BOUNDTYPE boundtype, /**< type of bound for var: lower or upper bound */
1429 SCIP_BOUNDCHGTYPE boundchgtype, /**< type of bound change: branching decision or inference */
1430 SCIP_Real lpsolval, /**< solval of variable in last LP on path to node, or SCIP_INVALID if unknown */
1431 SCIP_VAR* infervar, /**< variable that was changed (parent of var, or var itself), or NULL */
1432 SCIP_CONS* infercons, /**< constraint that deduced the bound change, or NULL */
1433 SCIP_PROP* inferprop, /**< propagator that deduced the bound change, or NULL */
1434 int inferinfo, /**< user information for inference to help resolving the conflict */
1435 SCIP_BOUNDTYPE inferboundtype /**< type of bound for inference var: lower or upper bound */
1436 )
1437 {
1438 SCIP_BOUNDCHG* boundchg;
1439
1440 assert(domchg != NULL);
1441 assert(var != NULL);
1442 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
1443 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
1444 assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, newbound, boundtype == SCIP_BOUNDTYPE_LOWER ? 1.0 : 0.0));
1445 assert(boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING || infervar != NULL);
1446 assert((boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER) == (infercons != NULL));
1447 assert(boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER || inferprop == NULL);
1448
1449 SCIPsetDebugMsg(set, "adding %s bound change <%s: %g> of variable <%s> to domain change at %p pointing to %p\n",
1450 boundtype == SCIP_BOUNDTYPE_LOWER ? "lower" : "upper", boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING ? "branching" : "inference",
1451 newbound, var->name, (void*)domchg, (void*)*domchg);
1452
1453 /* if domain change data doesn't exist, create it;
1454 * if domain change is static, convert it into dynamic change
1455 */
1456 if( *domchg == NULL )
1457 {
1458 SCIP_CALL( domchgCreate(domchg, blkmem) );
1459 }
1460 else if( (*domchg)->domchgdyn.domchgtype != SCIP_DOMCHGTYPE_DYNAMIC ) /*lint !e641*/
1461 {
1462 SCIP_CALL( domchgMakeDynamic(domchg, blkmem) );
1463 }
1464 assert(*domchg != NULL && (*domchg)->domchgdyn.domchgtype == SCIP_DOMCHGTYPE_DYNAMIC); /*lint !e641*/
1465
1466 /* get memory for additional bound change */
1467 SCIP_CALL( domchgEnsureBoundchgsSize(*domchg, blkmem, set, (*domchg)->domchgdyn.nboundchgs+1) );
1468
1469 /* fill in the bound change data */
1470 boundchg = &(*domchg)->domchgdyn.boundchgs[(*domchg)->domchgdyn.nboundchgs];
1471 boundchg->var = var;
1472 switch( boundchgtype )
1473 {
1474 case SCIP_BOUNDCHGTYPE_BRANCHING:
1475 boundchg->data.branchingdata.lpsolval = lpsolval;
1476 break;
1477 case SCIP_BOUNDCHGTYPE_CONSINFER:
1478 assert(infercons != NULL);
1479 boundchg->data.inferencedata.var = infervar;
1480 boundchg->data.inferencedata.reason.cons = infercons;
1481 boundchg->data.inferencedata.info = inferinfo;
1482 break;
1483 case SCIP_BOUNDCHGTYPE_PROPINFER:
1484 boundchg->data.inferencedata.var = infervar;
1485 boundchg->data.inferencedata.reason.prop = inferprop;
1486 boundchg->data.inferencedata.info = inferinfo;
1487 break;
1488 default:
1489 SCIPerrorMessage("invalid bound change type %d\n", boundchgtype);
1490 return SCIP_INVALIDDATA;
1491 }
1492
1493 boundchg->newbound = newbound;
1494 boundchg->boundchgtype = boundchgtype; /*lint !e641*/
1495 boundchg->boundtype = boundtype; /*lint !e641*/
1496 boundchg->inferboundtype = inferboundtype; /*lint !e641*/
1497 boundchg->applied = FALSE;
1498 boundchg->redundant = FALSE;
1499 (*domchg)->domchgdyn.nboundchgs++;
1500
1501 /* capture branching and inference data associated with the bound changes */
1502 SCIP_CALL( boundchgCaptureData(boundchg) );
1503
1504 #ifdef SCIP_DISABLED_CODE /* expensive debug check */
1505 #ifdef SCIP_MORE_DEBUG
1506 {
1507 int i;
1508 for( i = 0; i < (int)(*domchg)->domchgbound.nboundchgs; ++i )
1509 assert(SCIPvarGetType((*domchg)->domchgbound.boundchgs[i].var) == SCIP_VARTYPE_CONTINUOUS
1510 || SCIPsetIsFeasIntegral(set, (*domchg)->domchgbound.boundchgs[i].newbound));
1511 }
1512 #endif
1513 #endif
1514
1515 return SCIP_OKAY;
1516 }
1517
1518 /** adds hole change to domain changes */
1519 SCIP_RETCODE SCIPdomchgAddHolechg(
1520 SCIP_DOMCHG** domchg, /**< pointer to domain change data structure */
1521 BMS_BLKMEM* blkmem, /**< block memory */
1522 SCIP_SET* set, /**< global SCIP settings */
1523 SCIP_HOLELIST** ptr, /**< changed list pointer */
1524 SCIP_HOLELIST* newlist, /**< new value of list pointer */
1525 SCIP_HOLELIST* oldlist /**< old value of list pointer */
1526 )
1527 {
1528 SCIP_HOLECHG* holechg;
1529
1530 assert(domchg != NULL);
1531 assert(ptr != NULL);
1532
1533 /* if domain change data doesn't exist, create it;
1534 * if domain change is static, convert it into dynamic change
1535 */
1536 if( *domchg == NULL )
1537 {
1538 SCIP_CALL( domchgCreate(domchg, blkmem) );
1539 }
1540 else if( (*domchg)->domchgdyn.domchgtype != SCIP_DOMCHGTYPE_DYNAMIC ) /*lint !e641*/
1541 {
1542 SCIP_CALL( domchgMakeDynamic(domchg, blkmem) );
1543 }
1544 assert(*domchg != NULL && (*domchg)->domchgdyn.domchgtype == SCIP_DOMCHGTYPE_DYNAMIC); /*lint !e641*/
1545
1546 /* get memory for additional hole change */
1547 SCIP_CALL( domchgEnsureHolechgsSize(*domchg, blkmem, set, (*domchg)->domchgdyn.nholechgs+1) );
1548
1549 /* fill in the hole change data */
1550 holechg = &(*domchg)->domchgdyn.holechgs[(*domchg)->domchgdyn.nholechgs];
1551 holechg->ptr = ptr;
1552 holechg->newlist = newlist;
1553 holechg->oldlist = oldlist;
1554 (*domchg)->domchgdyn.nholechgs++;
1555
1556 return SCIP_OKAY;
1557 }
1558
1559
1560
1561
1562 /*
1563 * methods for variables
1564 */
1565
1566 /** returns adjusted lower bound value, which is rounded for integral variable types */
1567 static
1568 SCIP_Real adjustedLb(
1569 SCIP_SET* set, /**< global SCIP settings */
1570 SCIP_VARTYPE vartype, /**< type of variable */
1571 SCIP_Real lb /**< lower bound to adjust */
1572 )
1573 {
1574 if( lb < 0.0 && SCIPsetIsInfinity(set, -lb) )
1575 return -SCIPsetInfinity(set);
1576 else if( lb > 0.0 && SCIPsetIsInfinity(set, lb) )
1577 return SCIPsetInfinity(set);
1578 else if( vartype != SCIP_VARTYPE_CONTINUOUS )
1579 return SCIPsetFeasCeil(set, lb);
1580 else if( lb > 0.0 && lb < SCIPsetEpsilon(set) )
1581 return 0.0;
1582 else
1583 return lb;
1584 }
1585
1586 /** returns adjusted upper bound value, which is rounded for integral variable types */
1587 static
1588 SCIP_Real adjustedUb(
1589 SCIP_SET* set, /**< global SCIP settings */
1590 SCIP_VARTYPE vartype, /**< type of variable */
1591 SCIP_Real ub /**< upper bound to adjust */
1592 )
1593 {
1594 if( ub > 0.0 && SCIPsetIsInfinity(set, ub) )
1595 return SCIPsetInfinity(set);
1596 else if( ub < 0.0 && SCIPsetIsInfinity(set, -ub) )
1597 return -SCIPsetInfinity(set);
1598 else if( vartype != SCIP_VARTYPE_CONTINUOUS )
1599 return SCIPsetFeasFloor(set, ub);
1600 else if( ub < 0.0 && ub > -SCIPsetEpsilon(set) )
1601 return 0.0;
1602 else
1603 return ub;
1604 }
1605
1606 /** removes (redundant) cliques, implications and variable bounds of variable from all other variables' implications and variable
1607 * bounds arrays, and optionally removes them also from the variable itself
1608 */
1609 SCIP_RETCODE SCIPvarRemoveCliquesImplicsVbs(
1610 SCIP_VAR* var, /**< problem variable */
1611 BMS_BLKMEM* blkmem, /**< block memory */
1612 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
1613 SCIP_SET* set, /**< global SCIP settings */
1614 SCIP_Bool irrelevantvar, /**< has the variable become irrelevant? */
1615 SCIP_Bool onlyredundant, /**< should only the redundant implications and variable bounds be removed? */
1616 SCIP_Bool removefromvar /**< should the implications and variable bounds be removed from the var itself? */
1617 )
1618 {
1619 SCIP_Real lb;
1620 SCIP_Real ub;
1621
1622 assert(var != NULL);
1623 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
1624 assert(SCIPvarIsActive(var) || SCIPvarGetType(var) != SCIP_VARTYPE_BINARY);
1625
1626 lb = SCIPvarGetLbGlobal(var);
1627 ub = SCIPvarGetUbGlobal(var);
1628
1629 SCIPsetDebugMsg(set, "removing %s implications and vbounds of %s<%s>[%g,%g]\n",
1630 onlyredundant ? "redundant" : "all", irrelevantvar ? "irrelevant " : "", SCIPvarGetName(var), lb, ub);
1631
1632 /* remove implications of (fixed) binary variable */
1633 if( var->implics != NULL && (!onlyredundant || lb > 0.5 || ub < 0.5) )
1634 {
1635 SCIP_Bool varfixing;
1636
1637 assert(SCIPvarIsBinary(var));
1638
1639 varfixing = FALSE;
1640 do
1641 {
1642 SCIP_VAR** implvars;
1643 SCIP_BOUNDTYPE* impltypes;
1644 int nimpls;
1645 int i;
1646
1647 nimpls = SCIPimplicsGetNImpls(var->implics, varfixing);
1648 implvars = SCIPimplicsGetVars(var->implics, varfixing);
1649 impltypes = SCIPimplicsGetTypes(var->implics, varfixing);
1650
1651 for( i = 0; i < nimpls; i++ )
1652 {
1653 SCIP_VAR* implvar;
1654 SCIP_BOUNDTYPE impltype;
1655
1656 implvar = implvars[i];
1657 impltype = impltypes[i];
1658 assert(implvar != var);
1659
1660 /* remove for all implications z == 0 / 1 ==> x <= p / x >= p (x not binary)
1661 * the following variable bound from x's variable bounds
1662 * x <= b*z+d (z in vubs of x) , for z == 0 / 1 ==> x <= p
1663 * x >= b*z+d (z in vlbs of x) , for z == 0 / 1 ==> x >= p
1664 */
1665 if( impltype == SCIP_BOUNDTYPE_UPPER )
1666 {
1667 if( implvar->vubs != NULL ) /* implvar may have been aggregated in the mean time */
1668 {
1669 SCIPsetDebugMsg(set, "deleting variable bound: <%s> == %u ==> <%s> <= %g\n",
1670 SCIPvarGetName(var), varfixing, SCIPvarGetName(implvar),
1671 SCIPimplicsGetBounds(var->implics, varfixing)[i]);
1672 SCIP_CALL( SCIPvboundsDel(&implvar->vubs, blkmem, var, varfixing) );
1673 implvar->closestvblpcount = -1;
1674 var->closestvblpcount = -1;
1675 }
1676 }
1677 else
1678 {
1679 if( implvar->vlbs != NULL ) /* implvar may have been aggregated in the mean time */
1680 {
1681 SCIPsetDebugMsg(set, "deleting variable bound: <%s> == %u ==> <%s> >= %g\n",
1682 SCIPvarGetName(var), varfixing, SCIPvarGetName(implvar),
1683 SCIPimplicsGetBounds(var->implics, varfixing)[i]);
1684 SCIP_CALL( SCIPvboundsDel(&implvar->vlbs, blkmem, var, !varfixing) );
1685 implvar->closestvblpcount = -1;
1686 var->closestvblpcount = -1;
1687 }
1688 }
1689 }
1690 varfixing = !varfixing;
1691 }
1692 while( varfixing == TRUE );
1693
1694 if( removefromvar )
1695 {
1696 /* free the implications data structures */
1697 SCIPimplicsFree(&var->implics, blkmem);
1698 }
1699 }
1700
1701 /* remove the (redundant) variable lower bounds */
1702 if( var->vlbs != NULL )
1703 {
1704 SCIP_VAR** vars;
1705 SCIP_Real* coefs;
1706 SCIP_Real* constants;
1707 int nvbds;
1708 int newnvbds;
1709 int i;
1710
1711 nvbds = SCIPvboundsGetNVbds(var->vlbs);
1712 vars = SCIPvboundsGetVars(var->vlbs);
1713 coefs = SCIPvboundsGetCoefs(var->vlbs);
1714 constants = SCIPvboundsGetConstants(var->vlbs);
1715
1716 /* remove for all variable bounds x >= b*z+d the following implication from z's implications
1717 * z == ub ==> x >= b*ub + d , if b > 0
1718 * z == lb ==> x >= b*lb + d , if b < 0
1719 */
1720 newnvbds = 0;
1721 for( i = 0; i < nvbds; i++ )
1722 {
1723 SCIP_VAR* implvar;
1724 SCIP_Real coef;
1725
1726 assert(newnvbds <= i);
1727
1728 implvar = vars[i];
1729 assert(implvar != NULL);
1730
1731 coef = coefs[i];
1732 assert(!SCIPsetIsZero(set, coef));
1733
1734 /* check, if we want to remove the variable bound */
1735 if( onlyredundant )
1736 {
1737 SCIP_Real vbound;
1738
1739 vbound = MAX(coef * SCIPvarGetUbGlobal(implvar), coef * SCIPvarGetLbGlobal(implvar)) + constants[i]; /*lint !e666*/
1740 if( SCIPsetIsFeasGT(set, vbound, lb) )
1741 {
1742 /* the variable bound is not redundant: keep it */
1743 if( removefromvar )
1744 {
1745 if( newnvbds < i )
1746 {
1747 vars[newnvbds] = implvar;
1748 coefs[newnvbds] = coef;
1749 constants[newnvbds] = constants[i];
1750 }
1751 newnvbds++;
1752 }
1753 continue;
1754 }
1755 }
1756
1757 /* remove the corresponding implication */
1758 if( implvar->implics != NULL ) /* variable may have been aggregated in the mean time */
1759 {
1760 SCIPsetDebugMsg(set, "deleting implication: <%s> == %d ==> <%s> >= %g\n",
1761 SCIPvarGetName(implvar), (coef > 0.0), SCIPvarGetName(var), MAX(coef, 0.0) + constants[i]);
1762 SCIP_CALL( SCIPimplicsDel(&implvar->implics, blkmem, set, (coef > 0.0), var, SCIP_BOUNDTYPE_LOWER) );
1763 }
1764 if( coef > 0.0 && implvar->vubs != NULL ) /* implvar may have been aggregated in the mean time */
1765 {
1766 SCIPsetDebugMsg(set, "deleting variable upper bound from <%s> involving variable %s\n",
1767 SCIPvarGetName(implvar), SCIPvarGetName(var));
1768 SCIP_CALL( SCIPvboundsDel(&implvar->vubs, blkmem, var, FALSE) );
1769 implvar->closestvblpcount = -1;
1770 var->closestvblpcount = -1;
1771 }
1772 else if( coef < 0.0 && implvar->vlbs != NULL ) /* implvar may have been aggregated in the mean time */
1773 {
1774 SCIPsetDebugMsg(set, "deleting variable lower bound from <%s> involving variable %s\n",
1775 SCIPvarGetName(implvar), SCIPvarGetName(var));
1776 SCIP_CALL( SCIPvboundsDel(&implvar->vlbs, blkmem, var, TRUE) );
1777 implvar->closestvblpcount = -1;
1778 var->closestvblpcount = -1;
1779 }
1780 }
1781
1782 if( removefromvar )
1783 {
1784 /* update the number of variable bounds */
1785 SCIPvboundsShrink(&var->vlbs, blkmem, newnvbds);
1786 var->closestvblpcount = -1;
1787 }
1788 }
1789
1790 /**@todo in general, variable bounds like x >= b*z + d corresponding to an implication like z = ub ==> x >= b*ub + d
1791 * might be missing because we only add variable bounds with reasonably small value of b. thus, we currently
1792 * cannot remove such variables x from z's implications.
1793 */
1794
1795 /* remove the (redundant) variable upper bounds */
1796 if( var->vubs != NULL )
1797 {
1798 SCIP_VAR** vars;
1799 SCIP_Real* coefs;
1800 SCIP_Real* constants;
1801 int nvbds;
1802 int newnvbds;
1803 int i;
1804
1805 nvbds = SCIPvboundsGetNVbds(var->vubs);
1806 vars = SCIPvboundsGetVars(var->vubs);
1807 coefs = SCIPvboundsGetCoefs(var->vubs);
1808 constants = SCIPvboundsGetConstants(var->vubs);
1809
1810 /* remove for all variable bounds x <= b*z+d the following implication from z's implications
1811 * z == lb ==> x <= b*lb + d , if b > 0
1812 * z == ub ==> x <= b*ub + d , if b < 0
1813 */
1814 newnvbds = 0;
1815 for( i = 0; i < nvbds; i++ )
1816 {
1817 SCIP_VAR* implvar;
1818 SCIP_Real coef;
1819
1820 assert(newnvbds <= i);
1821
1822 implvar = vars[i];
1823 assert(implvar != NULL);
1824
1825 coef = coefs[i];
1826 assert(!SCIPsetIsZero(set, coef));
1827
1828 /* check, if we want to remove the variable bound */
1829 if( onlyredundant )
1830 {
1831 SCIP_Real vbound;
1832
1833 vbound = MIN(coef * SCIPvarGetUbGlobal(implvar), coef * SCIPvarGetLbGlobal(implvar)) + constants[i]; /*lint !e666*/
1834 if( SCIPsetIsFeasLT(set, vbound, ub) )
1835 {
1836 /* the variable bound is not redundant: keep it */
1837 if( removefromvar )
1838 {
1839 if( newnvbds < i )
1840 {
1841 vars[newnvbds] = implvar;
1842 coefs[newnvbds] = coefs[i];
1843 constants[newnvbds] = constants[i];
1844 }
1845 newnvbds++;
1846 }
1847 continue;
1848 }
1849 }
1850
1851 /* remove the corresponding implication */
1852 if( implvar->implics != NULL ) /* variable may have been aggregated in the mean time */
1853 {
1854 SCIPsetDebugMsg(set, "deleting implication: <%s> == %d ==> <%s> <= %g\n",
1855 SCIPvarGetName(implvar), (coef < 0.0), SCIPvarGetName(var), MIN(coef, 0.0) + constants[i]);
1856 SCIP_CALL( SCIPimplicsDel(&implvar->implics, blkmem, set, (coef < 0.0), var, SCIP_BOUNDTYPE_UPPER) );
1857 }
1858 if( coef < 0.0 && implvar->vubs != NULL ) /* implvar may have been aggregated in the mean time */
1859 {
1860 SCIPsetDebugMsg(set, "deleting variable upper bound from <%s> involving variable %s\n",
1861 SCIPvarGetName(implvar), SCIPvarGetName(var));
1862 SCIP_CALL( SCIPvboundsDel(&implvar->vubs, blkmem, var, TRUE) );
1863 implvar->closestvblpcount = -1;
1864 var->closestvblpcount = -1;
1865 }
1866 else if( coef > 0.0 && implvar->vlbs != NULL ) /* implvar may have been aggregated in the mean time */
1867 {
1868 SCIPsetDebugMsg(set, "deleting variable lower bound from <%s> involving variable %s\n",
1869 SCIPvarGetName(implvar), SCIPvarGetName(var));
1870 SCIP_CALL( SCIPvboundsDel(&implvar->vlbs, blkmem, var, FALSE) );
1871 implvar->closestvblpcount = -1;
1872 var->closestvblpcount = -1;
1873 }
1874 }
1875
1876 if( removefromvar )
1877 {
1878 /* update the number of variable bounds */
1879 SCIPvboundsShrink(&var->vubs, blkmem, newnvbds);
1880 var->closestvblpcount = -1;
1881 }
1882 }
1883
1884 /* remove the variable from all cliques */
1885 if( SCIPvarIsBinary(var) )
1886 SCIPcliquelistRemoveFromCliques(var->cliquelist, cliquetable, var, irrelevantvar);
1887
1888 /**@todo variable bounds like x <= b*z + d with z general integer are not removed from x's vbd arrays, because
1889 * z has no link (like in the binary case) to x
1890 */
1891
1892 return SCIP_OKAY;
1893 }
1894
1895 /** sets the variable name */
1896 static
1897 SCIP_RETCODE varSetName(
1898 SCIP_VAR* var, /**< problem variable */
1899 BMS_BLKMEM* blkmem, /**< block memory */
1900 SCIP_STAT* stat, /**< problem statistics, or NULL */
1901 const char* name /**< name of variable, or NULL for automatic name creation */
1902 )
1903 {
1904 assert(blkmem != NULL);
1905 assert(var != NULL);
1906
1907 if( name == NULL )
1908 {
1909 char s[SCIP_MAXSTRLEN];
1910
1911 assert(stat != NULL);
1912
1913 (void) SCIPsnprintf(s, SCIP_MAXSTRLEN, "_var%d_", stat->nvaridx);
1914 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->name, s, strlen(s)+1) );
1915 }
1916 else
1917 {
1918 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->name, name, strlen(name)+1) );
1919 }
1920
1921 return SCIP_OKAY;
1922 }
1923
1924
1925 /** creates variable; if variable is of integral type, fractional bounds are automatically rounded; an integer variable
1926 * with bounds zero and one is automatically converted into a binary variable
1927 */
1928 static
1929 SCIP_RETCODE varCreate(
1930 SCIP_VAR** var, /**< pointer to variable data */
1931 BMS_BLKMEM* blkmem, /**< block memory */
1932 SCIP_SET* set, /**< global SCIP settings */
1933 SCIP_STAT* stat, /**< problem statistics */
1934 const char* name, /**< name of variable, or NULL for automatic name creation */
1935 SCIP_Real lb, /**< lower bound of variable */
1936 SCIP_Real ub, /**< upper bound of variable */
1937 SCIP_Real obj, /**< objective function value */
1938 SCIP_VARTYPE vartype, /**< type of variable */
1939 SCIP_Bool initial, /**< should var's column be present in the initial root LP? */
1940 SCIP_Bool removable, /**< is var's column removable from the LP (due to aging or cleanup)? */
1941 SCIP_DECL_VARCOPY ((*varcopy)), /**< copies variable data if wanted to subscip, or NULL */
1942 SCIP_DECL_VARDELORIG ((*vardelorig)), /**< frees user data of original variable, or NULL */
1943 SCIP_DECL_VARTRANS ((*vartrans)), /**< creates transformed user data by transforming original user data, or NULL */
1944 SCIP_DECL_VARDELTRANS ((*vardeltrans)), /**< frees user data of transformed variable, or NULL */
1945 SCIP_VARDATA* vardata /**< user data for this specific variable */
1946 )
1947 {
1948 int i;
1949
1950 assert(var != NULL);
1951 assert(blkmem != NULL);
1952 assert(stat != NULL);
1953
1954 /* adjust bounds of variable */
1955 lb = adjustedLb(set, vartype, lb);
1956 ub = adjustedUb(set, vartype, ub);
1957
1958 /* convert [0,1]-integers into binary variables and check that binary variables have correct bounds */
1959 if( (SCIPsetIsEQ(set, lb, 0.0) || SCIPsetIsEQ(set, lb, 1.0))
1960 && (SCIPsetIsEQ(set, ub, 0.0) || SCIPsetIsEQ(set, ub, 1.0)) )
1961 {
1962 if( vartype == SCIP_VARTYPE_INTEGER )
1963 vartype = SCIP_VARTYPE_BINARY;
1964 }
1965 else
1966 {
1967 if( vartype == SCIP_VARTYPE_BINARY )
1968 {
1969 SCIPerrorMessage("invalid bounds [%.2g,%.2g] for binary variable <%s>\n", lb, ub, name);
1970 return SCIP_INVALIDDATA;
1971 }
1972 }
1973
1974 assert(vartype != SCIP_VARTYPE_BINARY || SCIPsetIsEQ(set, lb, 0.0) || SCIPsetIsEQ(set, lb, 1.0));
1975 assert(vartype != SCIP_VARTYPE_BINARY || SCIPsetIsEQ(set, ub, 0.0) || SCIPsetIsEQ(set, ub, 1.0));
1976
1977 SCIP_ALLOC( BMSallocBlockMemory(blkmem, var) );
1978
1979 /* set variable's name */
1980 SCIP_CALL( varSetName(*var, blkmem, stat, name) );
1981
1982 #ifndef NDEBUG
1983 (*var)->scip = set->scip;
1984 #endif
1985 (*var)->obj = obj;
1986 (*var)->unchangedobj = obj;
1987 (*var)->branchfactor = 1.0;
1988 (*var)->rootsol = 0.0;
1989 (*var)->bestrootsol = 0.0;
1990 (*var)->bestrootredcost = 0.0;
1991 (*var)->bestrootlpobjval = SCIP_INVALID;
1992 (*var)->relaxsol = 0.0;
1993 (*var)->nlpsol = 0.0;
1994 (*var)->primsolavg = 0.5 * (lb + ub);
1995 (*var)->conflictlb = SCIP_REAL_MIN;
1996 (*var)->conflictub = SCIP_REAL_MAX;
1997 (*var)->conflictrelaxedlb = (*var)->conflictlb;
1998 (*var)->conflictrelaxedub = (*var)->conflictub;
1999 (*var)->lazylb = -SCIPsetInfinity(set);
2000 (*var)->lazyub = SCIPsetInfinity(set);
2001 (*var)->glbdom.holelist = NULL;
2002 (*var)->glbdom.lb = lb;
2003 (*var)->glbdom.ub = ub;
2004 (*var)->locdom.holelist = NULL;
2005 (*var)->locdom.lb = lb;
2006 (*var)->locdom.ub = ub;
2007 (*var)->varcopy = varcopy;
2008 (*var)->vardelorig = vardelorig;
2009 (*var)->vartrans = vartrans;
2010 (*var)->vardeltrans = vardeltrans;
2011 (*var)->vardata = vardata;
2012 (*var)->parentvars = NULL;
2013 (*var)->negatedvar = NULL;
2014 (*var)->vlbs = NULL;
2015 (*var)->vubs = NULL;
2016 (*var)->implics = NULL;
2017 (*var)->cliquelist = NULL;
2018 (*var)->eventfilter = NULL;
2019 (*var)->lbchginfos = NULL;
2020 (*var)->ubchginfos = NULL;
2021 (*var)->index = stat->nvaridx;
2022 (*var)->probindex = -1;
2023 (*var)->pseudocandindex = -1;
2024 (*var)->eventqueueindexobj = -1;
2025 (*var)->eventqueueindexlb = -1;
2026 (*var)->eventqueueindexub = -1;
2027 (*var)->parentvarssize = 0;
2028 (*var)->nparentvars = 0;
2029 (*var)->nuses = 0;
2030 (*var)->branchpriority = 0;
2031 (*var)->branchdirection = SCIP_BRANCHDIR_AUTO; /*lint !e641*/
2032 (*var)->lbchginfossize = 0;
2033 (*var)->nlbchginfos = 0;
2034 (*var)->ubchginfossize = 0;
2035 (*var)->nubchginfos = 0;
2036 (*var)->conflictlbcount = 0;
2037 (*var)->conflictubcount = 0;
2038 (*var)->closestvlbidx = -1;
2039 (*var)->closestvubidx = -1;
2040 (*var)->closestvblpcount = -1;
2041 (*var)->initial = initial;
2042 (*var)->removable = removable;
2043 (*var)->deleted = FALSE;
2044 (*var)->donotaggr = FALSE;
2045 (*var)->donotmultaggr = FALSE;
2046 (*var)->vartype = vartype; /*lint !e641*/
2047 (*var)->pseudocostflag = FALSE;
2048 (*var)->eventqueueimpl = FALSE;
2049 (*var)->deletable = FALSE;
2050 (*var)->delglobalstructs = FALSE;
2051 (*var)->relaxationonly = FALSE;
2052
2053 for( i = 0; i < NLOCKTYPES; i++ )
2054 {
2055 (*var)->nlocksdown[i] = 0;
2056 (*var)->nlocksup[i] = 0;
2057 }
2058
2059 stat->nvaridx++;
2060
2061 /* create branching and inference history entries */
2062 SCIP_CALL( SCIPhistoryCreate(&(*var)->history, blkmem) );
2063 SCIP_CALL( SCIPhistoryCreate(&(*var)->historycrun, blkmem) );
2064
2065 /* the value based history is only created on demand */
2066 (*var)->valuehistory = NULL;
2067
2068 return SCIP_OKAY;
2069 }
2070
2071 /** creates and captures an original problem variable; an integer variable with bounds
2072 * zero and one is automatically converted into a binary variable
2073 */
2074 SCIP_RETCODE SCIPvarCreateOriginal(
2075 SCIP_VAR** var, /**< pointer to variable data */
2076 BMS_BLKMEM* blkmem, /**< block memory */
2077 SCIP_SET* set, /**< global SCIP settings */
2078 SCIP_STAT* stat, /**< problem statistics */
2079 const char* name, /**< name of variable, or NULL for automatic name creation */
2080 SCIP_Real lb, /**< lower bound of variable */
2081 SCIP_Real ub, /**< upper bound of variable */
2082 SCIP_Real obj, /**< objective function value */
2083 SCIP_VARTYPE vartype, /**< type of variable */
2084 SCIP_Bool initial, /**< should var's column be present in the initial root LP? */
2085 SCIP_Bool removable, /**< is var's column removable from the LP (due to aging or cleanup)? */
2086 SCIP_DECL_VARDELORIG ((*vardelorig)), /**< frees user data of original variable, or NULL */
2087 SCIP_DECL_VARTRANS ((*vartrans)), /**< creates transformed user data by transforming original user data, or NULL */
2088 SCIP_DECL_VARDELTRANS ((*vardeltrans)), /**< frees user data of transformed variable, or NULL */
2089 SCIP_DECL_VARCOPY ((*varcopy)), /**< copies variable data if wanted to subscip, or NULL */
2090 SCIP_VARDATA* vardata /**< user data for this specific variable */
2091 )
2092 {
2093 assert(var != NULL);
2094 assert(blkmem != NULL);
2095 assert(stat != NULL);
2096
2097 /* create variable */
2098 SCIP_CALL( varCreate(var, blkmem, set, stat, name, lb, ub, obj, vartype, initial, removable,
2099 varcopy, vardelorig, vartrans, vardeltrans, vardata) );
2100
2101 /* set variable status and data */
2102 (*var)->varstatus = SCIP_VARSTATUS_ORIGINAL; /*lint !e641*/
2103 (*var)->data.original.origdom.holelist = NULL;
2104 (*var)->data.original.origdom.lb = lb;
2105 (*var)->data.original.origdom.ub = ub;
2106 (*var)->data.original.transvar = NULL;
2107
2108 /* capture variable */
2109 SCIPvarCapture(*var);
2110
2111 return SCIP_OKAY;
2112 }
2113
2114 /** creates and captures a loose variable belonging to the transformed problem; an integer variable with bounds
2115 * zero and one is automatically converted into a binary variable
2116 */
2117 SCIP_RETCODE SCIPvarCreateTransformed(
2118 SCIP_VAR** var, /**< pointer to variable data */
2119 BMS_BLKMEM* blkmem, /**< block memory */
2120 SCIP_SET* set, /**< global SCIP settings */
2121 SCIP_STAT* stat, /**< problem statistics */
2122 const char* name, /**< name of variable, or NULL for automatic name creation */
2123 SCIP_Real lb, /**< lower bound of variable */
2124 SCIP_Real ub, /**< upper bound of variable */
2125 SCIP_Real obj, /**< objective function value */
2126 SCIP_VARTYPE vartype, /**< type of variable */
2127 SCIP_Bool initial, /**< should var's column be present in the initial root LP? */
2128 SCIP_Bool removable, /**< is var's column removable from the LP (due to aging or cleanup)? */
2129 SCIP_DECL_VARDELORIG ((*vardelorig)), /**< frees user data of original variable, or NULL */
2130 SCIP_DECL_VARTRANS ((*vartrans)), /**< creates transformed user data by transforming original user data, or NULL */
2131 SCIP_DECL_VARDELTRANS ((*vardeltrans)), /**< frees user data of transformed variable, or NULL */
2132 SCIP_DECL_VARCOPY ((*varcopy)), /**< copies variable data if wanted to subscip, or NULL */
2133 SCIP_VARDATA* vardata /**< user data for this specific variable */
2134 )
2135 {
2136 assert(var != NULL);
2137 assert(blkmem != NULL);
2138
2139 /* create variable */
2140 SCIP_CALL( varCreate(var, blkmem, set, stat, name, lb, ub, obj, vartype, initial, removable,
2141 varcopy, vardelorig, vartrans, vardeltrans, vardata) );
2142
2143 /* create event filter for transformed variable */
2144 SCIP_CALL( SCIPeventfilterCreate(&(*var)->eventfilter, blkmem) );
2145
2146 /* set variable status and data */
2147 (*var)->varstatus = SCIP_VARSTATUS_LOOSE; /*lint !e641*/
2148
2149 /* capture variable */
2150 SCIPvarCapture(*var);
2151
2152 return SCIP_OKAY;
2153 }
2154
2155 /** copies and captures a variable from source to target SCIP; an integer variable with bounds zero and one is
2156 * automatically converted into a binary variable; in case the variable data cannot be copied the variable is not
2157 * copied at all
2158 */
2159 SCIP_RETCODE SCIPvarCopy(
2160 SCIP_VAR** var, /**< pointer to store the target variable */
2161 BMS_BLKMEM* blkmem, /**< block memory */
2162 SCIP_SET* set, /**< global SCIP settings */
2163 SCIP_STAT* stat, /**< problem statistics */
2164 SCIP* sourcescip, /**< source SCIP data structure */
2165 SCIP_VAR* sourcevar, /**< source variable */
2166 SCIP_HASHMAP* varmap, /**< a hashmap to store the mapping of source variables corresponding
2167 * target variables */
2168 SCIP_HASHMAP* consmap, /**< a hashmap to store the mapping of source constraints to the corresponding
2169 * target constraints */
2170 SCIP_Bool global /**< should global or local bounds be used? */
2171 )
2172 {
2173 SCIP_VARDATA* targetdata;
2174 SCIP_RESULT result;
2175 SCIP_Real lb;
2176 SCIP_Real ub;
2177
2178 assert(set != NULL);
2179 assert(blkmem != NULL);
2180 assert(stat != NULL);
2181 assert(sourcescip != NULL);
2182 assert(sourcevar != NULL);
2183 assert(var != NULL);
2184 assert(set->stage == SCIP_STAGE_PROBLEM);
2185 assert(varmap != NULL);
2186 assert(consmap != NULL);
2187
2188 /** @todo copy hole lists */
2189 assert(global || SCIPvarGetHolelistLocal(sourcevar) == NULL);
2190 assert(!global || SCIPvarGetHolelistGlobal(sourcevar) == NULL);
2191
2192 result = SCIP_DIDNOTRUN;
2193 targetdata = NULL;
2194
2195 if( SCIPvarGetStatus(sourcevar) == SCIP_VARSTATUS_ORIGINAL )
2196 {
2197 lb = SCIPvarGetLbOriginal(sourcevar);
2198 ub = SCIPvarGetUbOriginal(sourcevar);
2199 }
2200 else
2201 {
2202 lb = global ? SCIPvarGetLbGlobal(sourcevar) : SCIPvarGetLbLocal(sourcevar);
2203 ub = global ? SCIPvarGetUbGlobal(sourcevar) : SCIPvarGetUbLocal(sourcevar);
2204 }
2205
2206 /* creates and captures the variable in the target SCIP and initialize callback methods and variable data to NULL */
2207 SCIP_CALL( SCIPvarCreateOriginal(var, blkmem, set, stat, SCIPvarGetName(sourcevar),
2208 lb, ub, SCIPvarGetObj(sourcevar), SCIPvarGetType(sourcevar),
2209 SCIPvarIsInitial(sourcevar), SCIPvarIsRemovable(sourcevar),
2210 NULL, NULL, NULL, NULL, NULL) );
2211 assert(*var != NULL);
2212
2213 /* directly copy donot(mult)aggr flag */
2214 (*var)->donotaggr = sourcevar->donotaggr;
2215 (*var)->donotmultaggr = sourcevar->donotmultaggr;
2216
2217 /* insert variable into mapping between source SCIP and the target SCIP */
2218 assert(!SCIPhashmapExists(varmap, sourcevar));
2219 SCIP_CALL( SCIPhashmapInsert(varmap, sourcevar, *var) );
2220
2221 /* in case there exists variable data and the variable data copy callback, try to copy variable data */
2222 if( sourcevar->vardata != NULL && sourcevar->varcopy != NULL )
2223 {
2224 SCIP_CALL( sourcevar->varcopy(set->scip, sourcescip, sourcevar, sourcevar->vardata,
2225 varmap, consmap, (*var), &targetdata, &result) );
2226
2227 /* evaluate result */
2228 if( result != SCIP_DIDNOTRUN && result != SCIP_SUCCESS )
2229 {
2230 SCIPerrorMessage("variable data copying method returned invalid result <%d>\n", result);
2231 return SCIP_INVALIDRESULT;
2232 }
2233
2234 assert(targetdata == NULL || result == SCIP_SUCCESS);
2235
2236 /* if copying was successful, add the created variable data to the variable as well as all callback methods */
2237 if( result == SCIP_SUCCESS )
2238 {
2239 (*var)->varcopy = sourcevar->varcopy;
2240 (*var)->vardelorig = sourcevar->vardelorig;
2241 (*var)->vartrans = sourcevar->vartrans;
2242 (*var)->vardeltrans = sourcevar->vardeltrans;
2243 (*var)->vardata = targetdata;
2244 }
2245 }
2246
2247 /* we initialize histories of the variables by copying the source variable-information */
2248 if( set->history_allowtransfer )
2249 {
2250 SCIPvarMergeHistories((*var), sourcevar, stat);
2251 }
2252
2253 /* in case the copying was successfully, add the created variable data to the variable as well as all callback
2254 * methods
2255 */
2256 if( result == SCIP_SUCCESS )
2257 {
2258 (*var)->varcopy = sourcevar->varcopy;
2259 (*var)->vardelorig = sourcevar->vardelorig;
2260 (*var)->vartrans = sourcevar->vartrans;
2261 (*var)->vardeltrans = sourcevar->vardeltrans;
2262 (*var)->vardata = targetdata;
2263 }
2264
2265 SCIPsetDebugMsg(set, "created copy <%s> of variable <%s>\n", SCIPvarGetName(*var), SCIPvarGetName(sourcevar));
2266
2267 return SCIP_OKAY;
2268 }
2269
2270 /** parse given string for a SCIP_Real bound */
2271 static
2272 SCIP_RETCODE parseValue(
2273 SCIP_SET* set, /**< global SCIP settings */
2274 const char* str, /**< string to parse */
2275 SCIP_Real* value, /**< pointer to store the parsed value */
2276 char** endptr /**< pointer to store the final string position if successfully parsed */
2277 )
2278 {
2279 /* first check for infinity value */
2280 if( strncmp(str, "+inf", 4) == 0 )
2281 {
2282 *value = SCIPsetInfinity(set);
2283 (*endptr) = (char*)str + 4;
2284 }
2285 else if( strncmp(str, "-inf", 4) == 0 )
2286 {
2287 *value = -SCIPsetInfinity(set);
2288 (*endptr) = (char*)str + 4;
2289 }
2290 else
2291 {
2292 if( !SCIPstrToRealValue(str, value, endptr) )
2293 {
2294 SCIPerrorMessage("expected value: %s.\n", str);
2295 return SCIP_READERROR;
2296 }
2297 }
2298
2299 return SCIP_OKAY;
2300 }
2301
2302 /** parse the characters as bounds */
2303 static
2304 SCIP_RETCODE parseBounds(
2305 SCIP_SET* set, /**< global SCIP settings */
2306 const char* str, /**< string to parse */
2307 char* type, /**< bound type (global, local, or lazy) */
2308 SCIP_Real* lb, /**< pointer to store the lower bound */
2309 SCIP_Real* ub, /**< pointer to store the upper bound */
2310 char** endptr /**< pointer to store the final string position if successfully parsed (or NULL if an error occured) */
2311 )
2312 {
2313 char token[SCIP_MAXSTRLEN];
2314 char* tmpend;
2315
2316 SCIPsetDebugMsg(set, "parsing bounds: '%s'\n", str);
2317
2318 /* get bound type */
2319 SCIPstrCopySection(str, ' ', ' ', type, SCIP_MAXSTRLEN, endptr);
2320 if ( *endptr == str
2321 || ( strncmp(type, "original", 8) != 0 && strncmp(type, "global", 6) != 0 && strncmp(type, "local", 5) != 0 && strncmp(type, "lazy", 4) != 0 ) )
2322 {
2323 SCIPsetDebugMsg(set, "unkown bound type\n");
2324 *endptr = NULL;
2325 return SCIP_OKAY;
2326 }
2327
2328 SCIPsetDebugMsg(set, "parsed bound type <%s>\n", type);
2329
2330 /* get lower bound */
2331 SCIPstrCopySection(str, '[', ',', token, SCIP_MAXSTRLEN, endptr);
2332 str = *endptr;
2333 SCIP_CALL( parseValue(set, token, lb, &tmpend) );
2334
2335 /* get upper bound */
2336 SCIP_CALL( parseValue(set, str, ub, endptr) );
2337
2338 SCIPsetDebugMsg(set, "parsed bounds: [%g,%g]\n", *lb, *ub);
2339
2340 /* skip end of bounds */
2341 while ( **endptr != '\0' && (**endptr == ']' || **endptr == ',') )
2342 ++(*endptr);
2343
2344 return SCIP_OKAY;
2345 }
2346
2347 /** parses a given string for a variable informations */
2348 static
2349 SCIP_RETCODE varParse(
2350 SCIP_SET* set, /**< global SCIP settings */
2351 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
2352 const char* str, /**< string to parse */
2353 char* name, /**< pointer to store the variable name */
2354 SCIP_Real* lb, /**< pointer to store the lower bound */
2355 SCIP_Real* ub, /**< pointer to store the upper bound */
2356 SCIP_Real* obj, /**< pointer to store the objective coefficient */
2357 SCIP_VARTYPE* vartype, /**< pointer to store the variable type */
2358 SCIP_Real* lazylb, /**< pointer to store if the lower bound is lazy */
2359 SCIP_Real* lazyub, /**< pointer to store if the upper bound is lazy */
2360 SCIP_Bool local, /**< should the local bound be applied */
2361 char** endptr, /**< pointer to store the final string position if successfully */
2362 SCIP_Bool* success /**< pointer store if the paring process was successful */
2363 )
2364 {
2365 SCIP_Real parsedlb;
2366 SCIP_Real parsedub;
2367 char token[SCIP_MAXSTRLEN];
2368 char* strptr;
2369 int i;
2370
2371 assert(lb != NULL);
2372 assert(ub != NULL);
2373 assert(obj != NULL);
2374 assert(vartype != NULL);
2375 assert(lazylb != NULL);
2376 assert(lazyub != NULL);
2377 assert(success != NULL);
2378
2379 (*success) = TRUE;
2380
2381 /* copy variable type */
2382 SCIPstrCopySection(str, '[', ']', token, SCIP_MAXSTRLEN, endptr);
2383 assert(*endptr != str);
2384 SCIPsetDebugMsg(set, "parsed variable type <%s>\n", token);
2385
2386 /* get variable type */
2387 if( strncmp(token, "binary", 3) == 0 )
2388 (*vartype) = SCIP_VARTYPE_BINARY;
2389 else if( strncmp(token, "integer", 3) == 0 )
2390 (*vartype) = SCIP_VARTYPE_INTEGER;
2391 else if( strncmp(token, "implicit", 3) == 0 )
2392 (*vartype) = SCIP_VARTYPE_IMPLINT;
2393 else if( strncmp(token, "continuous", 3) == 0 )
2394 (*vartype) = SCIP_VARTYPE_CONTINUOUS;
2395 else
2396 {
2397 SCIPmessagePrintWarning(messagehdlr, "unknown variable type\n");
2398 (*success) = FALSE;
2399 return SCIP_OKAY;
2400 }
2401
2402 /* move string pointer behind variable type */
2403 str = *endptr;
2404
2405 /* get variable name */
2406 SCIPstrCopySection(str, '<', '>', name, SCIP_MAXSTRLEN, endptr);
2407 assert(*endptr != str);
2408 SCIPsetDebugMsg(set, "parsed variable name <%s>\n", name);
2409
2410 /* move string pointer behind variable name */
2411 str = *endptr;
2412
2413 /* cut out objective coefficient */
2414 SCIPstrCopySection(str, '=', ',', token, SCIP_MAXSTRLEN, endptr);
2415
2416 /* move string pointer behind objective coefficient */
2417 str = *endptr;
2418
2419 /* get objective coefficient */
2420 if( !SCIPstrToRealValue(token, obj, endptr) )
2421 {
2422 *endptr = NULL;
2423 return SCIP_READERROR;
2424 }
2425
2426 SCIPsetDebugMsg(set, "parsed objective coefficient <%g>\n", *obj);
2427
2428 /* parse global/original bounds */
2429 SCIP_CALL( parseBounds(set, str, token, lb, ub, endptr) );
2430 if ( *endptr == NULL )
2431 {
2432 SCIPerrorMessage("Expected bound type: %s.\n", token);
2433 return SCIP_READERROR;
2434 }
2435 assert(strncmp(token, "global", 6) == 0 || strncmp(token, "original", 8) == 0);
2436
2437 /* initialize the lazy bound */
2438 *lazylb = -SCIPsetInfinity(set);
2439 *lazyub = SCIPsetInfinity(set);
2440
2441 /* store pointer */
2442 strptr = *endptr;
2443
2444 /* possibly parse optional local and lazy bounds */
2445 for( i = 0; i < 2 && *endptr != NULL && **endptr != '\0'; ++i )
2446 {
2447 /* start after previous bounds */
2448 strptr = *endptr;
2449
2450 /* parse global bounds */
2451 SCIP_CALL( parseBounds(set, strptr, token, &parsedlb, &parsedub, endptr) );
2452
2453 /* stop if parsing of bounds failed */
2454 if( *endptr == NULL )
2455 break;
2456
2457 if( strncmp(token, "local", 5) == 0 && local )
2458 {
2459 *lb = parsedlb;
2460 *ub = parsedub;
2461 }
2462 else if( strncmp(token, "lazy", 4) == 0 )
2463 {
2464 *lazylb = parsedlb;
2465 *lazyub = parsedub;
2466 }
2467 }
2468
2469 /* restore pointer */
2470 if ( *endptr == NULL )
2471 *endptr = strptr;
2472
2473 /* check bounds for binary variables */
2474 if ( (*vartype) == SCIP_VARTYPE_BINARY )
2475 {
2476 if ( SCIPsetIsLT(set, *lb, 0.0) || SCIPsetIsGT(set, *ub, 1.0) )
2477 {
2478 SCIPerrorMessage("Parsed invalid bounds for binary variable <%s>: [%f, %f].\n", name, *lb, *ub);
2479 return SCIP_READERROR;
2480 }
2481 if ( !SCIPsetIsInfinity(set, -(*lazylb)) && !SCIPsetIsInfinity(set, *lazyub) &&
2482 ( SCIPsetIsLT(set, *lazylb, 0.0) || SCIPsetIsGT(set, *lazyub, 1.0) ) )
2483 {
2484 SCIPerrorMessage("Parsed invalid lazy bounds for binary variable <%s>: [%f, %f].\n", name, *lazylb, *lazyub);
2485 return SCIP_READERROR;
2486 }
2487 }
2488
2489 return SCIP_OKAY;
2490 }
2491
2492 /** parses variable information (in cip format) out of a string; if the parsing process was successful an original
2493 * variable is created and captured; if variable is of integral type, fractional bounds are automatically rounded; an
2494 * integer variable with bounds zero and one is automatically converted into a binary variable
2495 */
2496 SCIP_RETCODE SCIPvarParseOriginal(
2497 SCIP_VAR** var, /**< pointer to variable data */
2498 BMS_BLKMEM* blkmem, /**< block memory */
2499 SCIP_SET* set, /**< global SCIP settings */
2500 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
2501 SCIP_STAT* stat, /**< problem statistics */
2502 const char* str, /**< string to parse */
2503 SCIP_Bool initial, /**< should var's column be present in the initial root LP? */
2504 SCIP_Bool removable, /**< is var's column removable from the LP (due to aging or cleanup)? */
2505 SCIP_DECL_VARCOPY ((*varcopy)), /**< copies variable data if wanted to subscip, or NULL */
2506 SCIP_DECL_VARDELORIG ((*vardelorig)), /**< frees user data of original variable */
2507 SCIP_DECL_VARTRANS ((*vartrans)), /**< creates transformed user data by transforming original user data */
2508 SCIP_DECL_VARDELTRANS ((*vardeltrans)), /**< frees user data of transformed variable */
2509 SCIP_VARDATA* vardata, /**< user data for this specific variable */
2510 char** endptr, /**< pointer to store the final string position if successfully */
2511 SCIP_Bool* success /**< pointer store if the paring process was successful */
2512 )
2513 {
2514 char name[SCIP_MAXSTRLEN];
2515 SCIP_Real lb;
2516 SCIP_Real ub;
2517 SCIP_Real obj;
2518 SCIP_VARTYPE vartype;
2519 SCIP_Real lazylb;
2520 SCIP_Real lazyub;
2521
2522 assert(var != NULL);
2523 assert(blkmem != NULL);
2524 assert(stat != NULL);
2525 assert(endptr != NULL);
2526 assert(success != NULL);
2527
2528 /* parse string in cip format for variable information */
2529 SCIP_CALL( varParse(set, messagehdlr, str, name, &lb, &ub, &obj, &vartype, &lazylb, &lazyub, FALSE, endptr, success) );
2530
2531 if( *success ) /*lint !e774*/
2532 {
2533 /* create variable */
2534 SCIP_CALL( varCreate(var, blkmem, set, stat, name, lb, ub, obj, vartype, initial, removable,
2535 varcopy, vardelorig, vartrans, vardeltrans, vardata) );
2536
2537 /* set variable status and data */
2538 (*var)->varstatus = SCIP_VARSTATUS_ORIGINAL; /*lint !e641*/
2539 (*var)->data.original.origdom.holelist = NULL;
2540 (*var)->data.original.origdom.lb = lb;
2541 (*var)->data.original.origdom.ub = ub;
2542 (*var)->data.original.transvar = NULL;
2543
2544 /* set lazy status of variable bounds */
2545 (*var)->lazylb = lazylb;
2546 (*var)->lazyub = lazyub;
2547
2548 /* capture variable */
2549 SCIPvarCapture(*var);
2550 }
2551
2552 return SCIP_OKAY;
2553 }
2554
2555 /** parses variable information (in cip format) out of a string; if the parsing process was successful a loose variable
2556 * belonging to the transformed problem is created and captured; if variable is of integral type, fractional bounds are
2557 * automatically rounded; an integer variable with bounds zero and one is automatically converted into a binary
2558 * variable
2559 */
2560 SCIP_RETCODE SCIPvarParseTransformed(
2561 SCIP_VAR** var, /**< pointer to variable data */
2562 BMS_BLKMEM* blkmem, /**< block memory */
2563 SCIP_SET* set, /**< global SCIP settings */
2564 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
2565 SCIP_STAT* stat, /**< problem statistics */
2566 const char* str, /**< string to parse */
2567 SCIP_Bool initial, /**< should var's column be present in the initial root LP? */
2568 SCIP_Bool removable, /**< is var's column removable from the LP (due to aging or cleanup)? */
2569 SCIP_DECL_VARCOPY ((*varcopy)), /**< copies variable data if wanted to subscip, or NULL */
2570 SCIP_DECL_VARDELORIG ((*vardelorig)), /**< frees user data of original variable */
2571 SCIP_DECL_VARTRANS ((*vartrans)), /**< creates transformed user data by transforming original user data */
2572 SCIP_DECL_VARDELTRANS ((*vardeltrans)), /**< frees user data of transformed variable */
2573 SCIP_VARDATA* vardata, /**< user data for this specific variable */
2574 char** endptr, /**< pointer to store the final string position if successfully */
2575 SCIP_Bool* success /**< pointer store if the paring process was successful */
2576 )
2577 {
2578 char name[SCIP_MAXSTRLEN];
2579 SCIP_Real lb;
2580 SCIP_Real ub;
2581 SCIP_Real obj;
2582 SCIP_VARTYPE vartype;
2583 SCIP_Real lazylb;
2584 SCIP_Real lazyub;
2585
2586 assert(var != NULL);
2587 assert(blkmem != NULL);
2588 assert(endptr != NULL);
2589 assert(success != NULL);
2590
2591 /* parse string in cip format for variable information */
2592 SCIP_CALL( varParse(set, messagehdlr, str, name, &lb, &ub, &obj, &vartype, &lazylb, &lazyub, TRUE, endptr, success) );
2593
2594 if( *success ) /*lint !e774*/
2595 {
2596 /* create variable */
2597 SCIP_CALL( varCreate(var, blkmem, set, stat, name, lb, ub, obj, vartype, initial, removable,
2598 varcopy, vardelorig, vartrans, vardeltrans, vardata) );
2599
2600 /* create event filter for transformed variable */
2601 SCIP_CALL( SCIPeventfilterCreate(&(*var)->eventfilter, blkmem) );
2602
2603 /* set variable status and data */
2604 (*var)->varstatus = SCIP_VARSTATUS_LOOSE; /*lint !e641*/
2605
2606 /* set lazy status of variable bounds */
2607 (*var)->lazylb = lazylb;
2608 (*var)->lazyub = lazyub;
2609
2610 /* capture variable */
2611 SCIPvarCapture(*var);
2612 }
2613
2614 return SCIP_OKAY;
2615 }
2616
2617 /** ensures, that parentvars array of var can store at least num entries */
2618 static
2619 SCIP_RETCODE varEnsureParentvarsSize(
2620 SCIP_VAR* var, /**< problem variable */
2621 BMS_BLKMEM* blkmem, /**< block memory */
2622 SCIP_SET* set, /**< global SCIP settings */
2623 int num /**< minimum number of entries to store */
2624 )
2625 {
2626 assert(var->nparentvars <= var->parentvarssize);
2627
2628 if( num > var->parentvarssize )
2629 {
2630 int newsize;
2631
2632 newsize = SCIPsetCalcMemGrowSize(set, num);
2633 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &var->parentvars, var->parentvarssize, newsize) );
2634 var->parentvarssize = newsize;
2635 }
2636 assert(num <= var->parentvarssize);
2637
2638 return SCIP_OKAY;
2639 }
2640
2641 /** adds variable to parent list of a variable and captures parent variable */
2642 static
2643 SCIP_RETCODE varAddParent(
2644 SCIP_VAR* var, /**< variable to add parent to */
2645 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
2646 SCIP_SET* set, /**< global SCIP settings */
2647 SCIP_VAR* parentvar /**< parent variable to add */
2648 )
2649 {
2650 assert(var != NULL);
2651 assert(parentvar != NULL);
2652
2653 /* the direct original counterpart must be stored as first parent */
2654 assert(var->nparentvars == 0 || SCIPvarGetStatus(parentvar) != SCIP_VARSTATUS_ORIGINAL);
2655
2656 SCIPsetDebugMsg(set, "adding parent <%s>[%p] to variable <%s>[%p] in slot %d\n",
2657 parentvar->name, (void*)parentvar, var->name, (void*)var, var->nparentvars);
2658
2659 SCIP_CALL( varEnsureParentvarsSize(var, blkmem, set, var->nparentvars+1) );
2660
2661 var->parentvars[var->nparentvars] = parentvar;
2662 var->nparentvars++;
2663
2664 SCIPvarCapture(parentvar);
2665
2666 return SCIP_OKAY;
2667 }
2668
2669 /** deletes and releases all variables from the parent list of a variable, frees the memory of parents array */
2670 static
2671 SCIP_RETCODE varFreeParents(
2672 SCIP_VAR** var, /**< pointer to variable */
2673 BMS_BLKMEM* blkmem, /**< block memory */
2674 SCIP_SET* set, /**< global SCIP settings */
2675 SCIP_EVENTQUEUE* eventqueue, /**< event queue (or NULL, if it's an original variable) */
2676 SCIP_LP* lp /**< current LP data (or NULL, if it's an original variable) */
2677 )
2678 {
2679 SCIP_VAR* parentvar;
2680 int i;
2681
2682 SCIPsetDebugMsg(set, "free parents of <%s>\n", (*var)->name);
2683
2684 /* release the parent variables and remove the link from the parent variable to the child */
2685 for( i = 0; i < (*var)->nparentvars; ++i )
2686 {
2687 assert((*var)->parentvars != NULL);
2688 parentvar = (*var)->parentvars[i];
2689 assert(parentvar != NULL);
2690
2691 switch( SCIPvarGetStatus(parentvar) )
2692 {
2693 case SCIP_VARSTATUS_ORIGINAL:
2694 assert(parentvar->data.original.transvar == *var);
2695 assert(&parentvar->data.original.transvar != var);
2696 parentvar->data.original.transvar = NULL;
2697 break;
2698
2699 case SCIP_VARSTATUS_AGGREGATED:
2700 assert(parentvar->data.aggregate.var == *var);
2701 assert(&parentvar->data.aggregate.var != var);
2702 parentvar->data.aggregate.var = NULL;
2703 break;
2704
2705 #if 0
2706 /* The following code is unclear: should the current variable be removed from its parents? */
2707 case SCIP_VARSTATUS_MULTAGGR:
2708 assert(parentvar->data.multaggr.vars != NULL);
2709 for( v = 0; v < parentvar->data.multaggr.nvars && parentvar->data.multaggr.vars[v] != *var; ++v )
2710 {}
2711 assert(v < parentvar->data.multaggr.nvars && parentvar->data.multaggr.vars[v] == *var);
2712 if( v < parentvar->data.multaggr.nvars-1 )
2713 {
2714 parentvar->data.multaggr.vars[v] = parentvar->data.multaggr.vars[parentvar->data.multaggr.nvars-1];
2715 parentvar->data.multaggr.scalars[v] = parentvar->data.multaggr.scalars[parentvar->data.multaggr.nvars-1];
2716 }
2717 parentvar->data.multaggr.nvars--;
2718 break;
2719 #endif
2720
2721 case SCIP_VARSTATUS_NEGATED:
2722 assert(parentvar->negatedvar == *var);
2723 assert((*var)->negatedvar == parentvar);
2724 parentvar->negatedvar = NULL;
2725 (*var)->negatedvar = NULL;
2726 break;
2727
2728 default:
2729 SCIPerrorMessage("parent variable is neither ORIGINAL, AGGREGATED nor NEGATED\n");
2730 return SCIP_INVALIDDATA;
2731 } /*lint !e788*/
2732
2733 SCIP_CALL( SCIPvarRelease(&(*var)->parentvars[i], blkmem, set, eventqueue, lp) );
2734 }
2735
2736 /* free parentvars array */
2737 BMSfreeBlockMemoryArrayNull(blkmem, &(*var)->parentvars, (*var)->parentvarssize);
2738
2739 return SCIP_OKAY;
2740 }
2741
2742 /** frees a variable */
2743 static
2744 SCIP_RETCODE varFree(
2745 SCIP_VAR** var, /**< pointer to variable */
2746 BMS_BLKMEM* blkmem, /**< block memory */
2747 SCIP_SET* set, /**< global SCIP settings */
2748 SCIP_EVENTQUEUE* eventqueue, /**< event queue (may be NULL, if it's not a column variable) */
2749 SCIP_LP* lp /**< current LP data (may be NULL, if it's not a column variable) */
2750 )
2751 {
2752 assert(var != NULL);
2753 assert(*var != NULL);
2754 assert(SCIPvarGetStatus(*var) != SCIP_VARSTATUS_COLUMN || &(*var)->data.col->var != var);
2755 assert((*var)->nuses == 0);
2756 assert((*var)->probindex == -1);
2757 assert((*var)->nlocksup[SCIP_LOCKTYPE_MODEL] == 0);
2758 assert((*var)->nlocksdown[SCIP_LOCKTYPE_MODEL] == 0);
2759
2760 SCIPsetDebugMsg(set, "free variable <%s> with status=%d\n", (*var)->name, SCIPvarGetStatus(*var));
2761
2762 switch( SCIPvarGetStatus(*var) )
2763 {
2764 case SCIP_VARSTATUS_ORIGINAL:
2765 assert((*var)->data.original.transvar == NULL); /* cannot free variable, if transformed variable is still existing */
2766 holelistFree(&(*var)->data.original.origdom.holelist, blkmem);
2767 assert((*var)->data.original.origdom.holelist == NULL);
2768 break;
2769 case SCIP_VARSTATUS_LOOSE:
2770 break;
2771 case SCIP_VARSTATUS_COLUMN:
2772 SCIP_CALL( SCIPcolFree(&(*var)->data.col, blkmem, set, eventqueue, lp) ); /* free corresponding LP column */
2773 break;
2774 case SCIP_VARSTATUS_FIXED:
2775 case SCIP_VARSTATUS_AGGREGATED:
2776 break;
2777 case SCIP_VARSTATUS_MULTAGGR:
2778 BMSfreeBlockMemoryArray(blkmem, &(*var)->data.multaggr.vars, (*var)->data.multaggr.varssize);
2779 BMSfreeBlockMemoryArray(blkmem, &(*var)->data.multaggr.scalars, (*var)->data.multaggr.varssize);
2780 break;
2781 case SCIP_VARSTATUS_NEGATED:
2782 break;
2783 default:
2784 SCIPerrorMessage("unknown variable status\n");
2785 return SCIP_INVALIDDATA;
2786 }
2787
2788 /* release all parent variables and free the parentvars array */
2789 SCIP_CALL( varFreeParents(var, blkmem, set, eventqueue, lp) );
2790
2791 /* free user data */
2792 if( SCIPvarGetStatus(*var) == SCIP_VARSTATUS_ORIGINAL )
2793 {
2794 if( (*var)->vardelorig != NULL )
2795 {
2796 SCIP_CALL( (*var)->vardelorig(set->scip, *var, &(*var)->vardata) );
2797 }
2798 }
2799 else
2800 {
2801 if( (*var)->vardeltrans != NULL )
2802 {
2803 SCIP_CALL( (*var)->vardeltrans(set->scip, *var, &(*var)->vardata) );
2804 }
2805 }
2806
2807 /* free event filter */
2808 if( (*var)->eventfilter != NULL )
2809 {
2810 SCIP_CALL( SCIPeventfilterFree(&(*var)->eventfilter, blkmem, set) );
2811 }
2812 assert((*var)->eventfilter == NULL);
2813
2814 /* free hole lists */
2815 holelistFree(&(*var)->glbdom.holelist, blkmem);
2816 holelistFree(&(*var)->locdom.holelist, blkmem);
2817 assert((*var)->glbdom.holelist == NULL);
2818 assert((*var)->locdom.holelist == NULL);
2819
2820 /* free variable bounds data structures */
2821 SCIPvboundsFree(&(*var)->vlbs, blkmem);
2822 SCIPvboundsFree(&(*var)->vubs, blkmem);
2823
2824 /* free implications data structures */
2825 SCIPimplicsFree(&(*var)->implics, blkmem);
2826
2827 /* free clique list data structures */
2828 SCIPcliquelistFree(&(*var)->cliquelist, blkmem);
2829
2830 /* free bound change information arrays */
2831 BMSfreeBlockMemoryArrayNull(blkmem, &(*var)->lbchginfos, (*var)->lbchginfossize);
2832 BMSfreeBlockMemoryArrayNull(blkmem, &(*var)->ubchginfos, (*var)->ubchginfossize);
2833
2834 /* free branching and inference history entries */
2835 SCIPhistoryFree(&(*var)->history, blkmem);
2836 SCIPhistoryFree(&(*var)->historycrun, blkmem);
2837 SCIPvaluehistoryFree(&(*var)->valuehistory, blkmem);
2838
2839 /* free variable data structure */
2840 BMSfreeBlockMemoryArray(blkmem, &(*var)->name, strlen((*var)->name)+1);
2841 BMSfreeBlockMemory(blkmem, var);
2842
2843 return SCIP_OKAY;
2844 }
2845
2846 /** increases usage counter of variable */
2847 void SCIPvarCapture(
2848 SCIP_VAR* var /**< variable */
2849 )
2850 {
2851 assert(var != NULL);
2852 assert(var->nuses >= 0);
2853
2854 SCIPdebugMessage("capture variable <%s> with nuses=%d\n", var->name, var->nuses);
2855 var->nuses++;
2856
2857 #ifdef DEBUGUSES_VARNAME
2858 if( strcmp(var->name, DEBUGUSES_VARNAME) == 0
2859 #ifdef DEBUGUSES_PROBNAME
2860 && ((var->scip->transprob != NULL && strcmp(SCIPprobGetName(var->scip->transprob), DEBUGUSES_PROBNAME) == 0) ||
2861 strcmp(SCIPprobGetName(var->scip->origprob), DEBUGUSES_PROBNAME) == 0)
2862 #endif
2863 )
2864 {
2865 printf("Captured variable " DEBUGUSES_VARNAME " in SCIP %p, now %d uses; captured at\n", (void*)var->scip, var->nuses);
2866 print_backtrace();
2867 }
2868 #endif
2869 }
2870
2871 /** decreases usage counter of variable, and frees memory if necessary */
2872 SCIP_RETCODE SCIPvarRelease(
2873 SCIP_VAR** var, /**< pointer to variable */
2874 BMS_BLKMEM* blkmem, /**< block memory */
2875 SCIP_SET* set, /**< global SCIP settings */
2876 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
2877 SCIP_LP* lp /**< current LP data (or NULL, if it's an original variable) */
2878 )
2879 {
2880 assert(var != NULL);
2881 assert(*var != NULL);
2882 assert((*var)->nuses >= 1);
2883 assert(blkmem != NULL);
2884 assert((*var)->scip == set->scip);
2885
2886 SCIPsetDebugMsg(set, "release variable <%s> with nuses=%d\n", (*var)->name, (*var)->nuses);
2887 (*var)->nuses--;
2888
2889 #ifdef DEBUGUSES_VARNAME
2890 if( strcmp((*var)->name, DEBUGUSES_VARNAME) == 0
2891 #ifdef DEBUGUSES_PROBNAME
2892 && (((*var)->scip->transprob != NULL && strcmp(SCIPprobGetName((*var)->scip->transprob), DEBUGUSES_PROBNAME) == 0) ||
2893 strcmp(SCIPprobGetName((*var)->scip->origprob), DEBUGUSES_PROBNAME) == 0)
2894 #endif
2895 )
2896 {
2897 printf("Released variable " DEBUGUSES_VARNAME " in SCIP %p, now %d uses; released at\n", (void*)(*var)->scip, (*var)->nuses);
2898 print_backtrace();
2899 }
2900 #endif
2901
2902 if( (*var)->nuses == 0 )
2903 {
2904 SCIP_CALL( varFree(var, blkmem, set, eventqueue, lp) );
2905 }
2906
2907 *var = NULL;
2908
2909 return SCIP_OKAY;
2910 }
2911
2912 /** change variable name */
2913 SCIP_RETCODE SCIPvarChgName(
2914 SCIP_VAR* var, /**< problem variable */
2915 BMS_BLKMEM* blkmem, /**< block memory */
2916 const char* name /**< name of variable */
2917 )
2918 {
2919 assert(name != NULL);
2920
2921 /* remove old variable name */
2922 BMSfreeBlockMemoryArray(blkmem, &var->name, strlen(var->name)+1);
2923
2924 /* set new variable name */
2925 SCIP_CALL( varSetName(var, blkmem, NULL, name) );
2926
2927 return SCIP_OKAY;
2928 }
2929
2930 /** initializes variable data structure for solving */
2931 void SCIPvarInitSolve(
2932 SCIP_VAR* var /**< problem variable */
2933 )
2934 {
2935 assert(var != NULL);
2936
2937 SCIPhistoryReset(var->historycrun);
2938 var->conflictlbcount = 0;
2939 var->conflictubcount = 0;
2940 }
2941
2942 /** outputs the given bounds into the file stream */
2943 static
2944 void printBounds(
2945 SCIP_SET* set, /**< global SCIP settings */
2946 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
2947 FILE* file, /**< output file (or NULL for standard output) */
2948 SCIP_Real lb, /**< lower bound */
2949 SCIP_Real ub, /**< upper bound */
2950 const char* name /**< bound type name */
2951 )
2952 {
2953 assert(set != NULL);
2954
2955 SCIPmessageFPrintInfo(messagehdlr, file, ", %s=", name);
2956 if( SCIPsetIsInfinity(set, lb) )
2957 SCIPmessageFPrintInfo(messagehdlr, file, "[+inf,");
2958 else if( SCIPsetIsInfinity(set, -lb) )
2959 SCIPmessageFPrintInfo(messagehdlr, file, "[-inf,");
2960 else
2961 SCIPmessageFPrintInfo(messagehdlr, file, "[%.15g,", lb);
2962 if( SCIPsetIsInfinity(set, ub) )
2963 SCIPmessageFPrintInfo(messagehdlr, file, "+inf]");
2964 else if( SCIPsetIsInfinity(set, -ub) )
2965 SCIPmessageFPrintInfo(messagehdlr, file, "-inf]");
2966 else
2967 SCIPmessageFPrintInfo(messagehdlr, file, "%.15g]", ub);
2968 }
2969
2970 /** prints hole list to file stream */
2971 static
2972 void printHolelist(
2973 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
2974 FILE* file, /**< output file (or NULL for standard output) */
2975 SCIP_HOLELIST* holelist, /**< hole list pointer to hole of interest */
2976 const char* name /**< hole type name */
2977 )
2978 { /*lint --e{715}*/
2979 SCIP_Real left;
2980 SCIP_Real right;
2981
2982 if( holelist == NULL )
2983 return;
2984
2985 left = SCIPholelistGetLeft(holelist);
2986 right = SCIPholelistGetRight(holelist);
2987
2988 /* display first hole */
2989 SCIPmessageFPrintInfo(messagehdlr, file, ", %s=(%g,%g)", name, left, right);
2990 holelist = SCIPholelistGetNext(holelist);
2991
2992 while(holelist != NULL )
2993 {
2994 left = SCIPholelistGetLeft(holelist);
2995 right = SCIPholelistGetRight(holelist);
2996
2997 /* display hole */
2998 SCIPmessageFPrintInfo(messagehdlr, file, "(%g,%g)", left, right);
2999
3000 /* get next hole */
3001 holelist = SCIPholelistGetNext(holelist);
3002 }
3003 }
3004
3005 /** outputs variable information into file stream */
3006 SCIP_RETCODE SCIPvarPrint(
3007 SCIP_VAR* var, /**< problem variable */
3008 SCIP_SET* set, /**< global SCIP settings */
3009 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
3010 FILE* file /**< output file (or NULL for standard output) */
3011 )
3012 {
3013 SCIP_HOLELIST* holelist;
3014 SCIP_Real lb;
3015 SCIP_Real ub;
3016 int i;
3017
3018 assert(var != NULL);
3019 assert(var->scip == set->scip);
3020
3021 /* type of variable */
3022 switch( SCIPvarGetType(var) )
3023 {
3024 case SCIP_VARTYPE_BINARY:
3025 SCIPmessageFPrintInfo(messagehdlr, file, " [binary]");
3026 break;
3027 case SCIP_VARTYPE_INTEGER:
3028 SCIPmessageFPrintInfo(messagehdlr, file, " [integer]");
3029 break;
3030 case SCIP_VARTYPE_IMPLINT:
3031 SCIPmessageFPrintInfo(messagehdlr, file, " [implicit]");
3032 break;
3033 case SCIP_VARTYPE_CONTINUOUS:
3034 SCIPmessageFPrintInfo(messagehdlr, file, " [continuous]");
3035 break;
3036 default:
3037 SCIPerrorMessage("unknown variable type\n");
3038 SCIPABORT();
3039 return SCIP_ERROR; /*lint !e527*/
3040 }
3041
3042 /* name */
3043 SCIPmessageFPrintInfo(messagehdlr, file, " <%s>:", var->name);
3044
3045 /* objective value */
3046 SCIPmessageFPrintInfo(messagehdlr, file, " obj=%.15g", var->obj);
3047
3048 /* bounds (global bounds for transformed variables, original bounds for original variables) */
3049 if( !SCIPvarIsTransformed(var) )
3050 {
3051 /* output original bound */
3052 lb = SCIPvarGetLbOriginal(var);
3053 ub = SCIPvarGetUbOriginal(var);
3054 printBounds(set, messagehdlr, file, lb, ub, "original bounds");
3055
3056 /* output lazy bound */
3057 lb = SCIPvarGetLbLazy(var);
3058 ub = SCIPvarGetUbLazy(var);
3059
3060 /* only display the lazy bounds if they are different from [-infinity,infinity] */
3061 if( !SCIPsetIsInfinity(set, -lb) || !SCIPsetIsInfinity(set, ub) )
3062 printBounds(set, messagehdlr, file, lb, ub, "lazy bounds");
3063
3064 holelist = SCIPvarGetHolelistOriginal(var);
3065 printHolelist(messagehdlr, file, holelist, "original holes");
3066 }
3067 else
3068 {
3069 /* output global bound */
3070 lb = SCIPvarGetLbGlobal(var);
3071 ub = SCIPvarGetUbGlobal(var);
3072 printBounds(set, messagehdlr, file, lb, ub, "global bounds");
3073
3074 /* output local bound */
3075 lb = SCIPvarGetLbLocal(var);
3076 ub = SCIPvarGetUbLocal(var);
3077 printBounds(set, messagehdlr, file, lb, ub, "local bounds");
3078
3079 /* output lazy bound */
3080 lb = SCIPvarGetLbLazy(var);
3081 ub = SCIPvarGetUbLazy(var);
3082
3083 /* only display the lazy bounds if they are different from [-infinity,infinity] */
3084 if( !SCIPsetIsInfinity(set, -lb) || !SCIPsetIsInfinity(set, ub) )
3085 printBounds(set, messagehdlr, file, lb, ub, "lazy bounds");
3086
3087 /* global hole list */
3088 holelist = SCIPvarGetHolelistGlobal(var);
3089 printHolelist(messagehdlr, file, holelist, "global holes");
3090
3091 /* local hole list */
3092 holelist = SCIPvarGetHolelistLocal(var);
3093 printHolelist(messagehdlr, file, holelist, "local holes");
3094 }
3095
3096 /* fixings and aggregations */
3097 switch( SCIPvarGetStatus(var) )
3098 {
3099 case SCIP_VARSTATUS_ORIGINAL:
3100 case SCIP_VARSTATUS_LOOSE:
3101 case SCIP_VARSTATUS_COLUMN:
3102 break;
3103
3104 case SCIP_VARSTATUS_FIXED:
3105 SCIPmessageFPrintInfo(messagehdlr, file, ", fixed:");
3106 if( SCIPsetIsInfinity(set, var->glbdom.lb) )
3107 SCIPmessageFPrintInfo(messagehdlr, file, "+inf");
3108 else if( SCIPsetIsInfinity(set, -var->glbdom.lb) )
3109 SCIPmessageFPrintInfo(messagehdlr, file, "-inf");
3110 else
3111 SCIPmessageFPrintInfo(messagehdlr, file, "%.15g", var->glbdom.lb);
3112 break;
3113
3114 case SCIP_VARSTATUS_AGGREGATED:
3115 SCIPmessageFPrintInfo(messagehdlr, file, ", aggregated:");
3116 if( !SCIPsetIsZero(set, var->data.aggregate.constant) )
3117 SCIPmessageFPrintInfo(messagehdlr, file, " %.15g", var->data.aggregate.constant);
3118 SCIPmessageFPrintInfo(messagehdlr, file, " %+.15g<%s>", var->data.aggregate.scalar, SCIPvarGetName(var->data.aggregate.var));
3119 break;
3120
3121 case SCIP_VARSTATUS_MULTAGGR:
3122 SCIPmessageFPrintInfo(messagehdlr, file, ", aggregated:");
3123 if( var->data.multaggr.nvars == 0 || !SCIPsetIsZero(set, var->data.multaggr.constant) )
3124 SCIPmessageFPrintInfo(messagehdlr, file, " %.15g", var->data.multaggr.constant);
3125 for( i = 0; i < var->data.multaggr.nvars; ++i )
3126 SCIPmessageFPrintInfo(messagehdlr, file, " %+.15g<%s>", var->data.multaggr.scalars[i], SCIPvarGetName(var->data.multaggr.vars[i]));
3127 break;
3128
3129 case SCIP_VARSTATUS_NEGATED:
3130 SCIPmessageFPrintInfo(messagehdlr, file, ", negated: %.15g - <%s>", var->data.negate.constant, SCIPvarGetName(var->negatedvar));
3131 break;
3132
3133 default:
3134 SCIPerrorMessage("unknown variable status\n");
3135 SCIPABORT();
3136 return SCIP_ERROR; /*lint !e527*/
3137 }
3138
3139 SCIPmessageFPrintInfo(messagehdlr, file, "\n");
3140
3141 return SCIP_OKAY;
3142 }
3143
3144 /** issues a VARUNLOCKED event on the given variable */
3145 static
3146 SCIP_RETCODE varEventVarUnlocked(
3147 SCIP_VAR* var, /**< problem variable to change */
3148 BMS_BLKMEM* blkmem, /**< block memory */
3149 SCIP_SET* set, /**< global SCIP settings */
3150 SCIP_EVENTQUEUE* eventqueue /**< event queue */
3151 )
3152 {
3153 SCIP_EVENT* event;
3154
3155 assert(var != NULL);
3156 assert(var->nlocksdown[SCIP_LOCKTYPE_MODEL] <= 1 && var->nlocksup[SCIP_LOCKTYPE_MODEL] <= 1);
3157 assert(var->scip == set->scip);
3158
3159 /* issue VARUNLOCKED event on variable */
3160 SCIP_CALL( SCIPeventCreateVarUnlocked(&event, blkmem, var) );
3161 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, NULL, &event) );
3162
3163 return SCIP_OKAY;
3164 }
3165
3166 /** modifies lock numbers for rounding */
3167 SCIP_RETCODE SCIPvarAddLocks(
3168 SCIP_VAR* var, /**< problem variable */
3169 BMS_BLKMEM* blkmem, /**< block memory */
3170 SCIP_SET* set, /**< global SCIP settings */
3171 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
3172 SCIP_LOCKTYPE locktype, /**< type of the variable locks */
3173 int addnlocksdown, /**< increase in number of rounding down locks */
3174 int addnlocksup /**< increase in number of rounding up locks */
3175 )
3176 {
3177 SCIP_VAR* lockvar;
3178
3179 assert(var != NULL);
3180 assert((int)locktype >= 0 && (int)locktype < (int)NLOCKTYPES); /*lint !e685 !e568 !e587 !e650*/
3181 assert(var->nlocksup[locktype] >= 0);
3182 assert(var->nlocksdown[locktype] >= 0);
3183 assert(var->scip == set->scip);
3184
3185 if( addnlocksdown == 0 && addnlocksup == 0 )
3186 return SCIP_OKAY;
3187
3188 #ifdef SCIP_DEBUG
3189 SCIPsetDebugMsg(set, "add rounding locks %d/%d to variable <%s> (locks=%d/%d, type=%u)\n",
3190 addnlocksdown, addnlocksup, var->name, var->nlocksdown[locktype], var->nlocksup[locktype], locktype);
3191 #endif
3192
3193 lockvar = var;
3194
3195 while( TRUE ) /*lint !e716 */
3196 {
3197 assert(lockvar != NULL);
3198
3199 switch( SCIPvarGetStatus(lockvar) )
3200 {
3201 case SCIP_VARSTATUS_ORIGINAL:
3202 if( lockvar->data.original.transvar != NULL )
3203 {
3204 lockvar = lockvar->data.original.transvar;
3205 break;
3206 }
3207 else
3208 {
3209 lockvar->nlocksdown[locktype] += addnlocksdown;
3210 lockvar->nlocksup[locktype] += addnlocksup;
3211
3212 assert(lockvar->nlocksdown[locktype] >= 0);
3213 assert(lockvar->nlocksup[locktype] >= 0);
3214
3215 return SCIP_OKAY;
3216 }
3217 case SCIP_VARSTATUS_LOOSE:
3218 case SCIP_VARSTATUS_COLUMN:
3219 case SCIP_VARSTATUS_FIXED:
3220 lockvar->nlocksdown[locktype] += addnlocksdown;
3221 lockvar->nlocksup[locktype] += addnlocksup;
3222
3223 assert(lockvar->nlocksdown[locktype] >= 0);
3224 assert(lockvar->nlocksup[locktype] >= 0);
3225
3226 if( locktype == SCIP_LOCKTYPE_MODEL && lockvar->nlocksdown[locktype] <= 1
3227 && lockvar->nlocksup[locktype] <= 1 )
3228 {
3229 SCIP_CALL( varEventVarUnlocked(lockvar, blkmem, set, eventqueue) );
3230 }
3231
3232 return SCIP_OKAY;
3233 case SCIP_VARSTATUS_AGGREGATED:
3234 assert(!lockvar->donotaggr);
3235
3236 if( lockvar->data.aggregate.scalar < 0.0 )
3237 {
3238 int tmp = addnlocksup;
3239
3240 addnlocksup = addnlocksdown;
3241 addnlocksdown = tmp;
3242 }
3243
3244 lockvar = lockvar->data.aggregate.var;
3245 break;
3246 case SCIP_VARSTATUS_MULTAGGR:
3247 {
3248 int v;
3249
3250 assert(!lockvar->donotmultaggr);
3251
3252 lockvar->nlocksdown[locktype] += addnlocksdown;
3253 lockvar->nlocksup[locktype] += addnlocksup;
3254
3255 assert(lockvar->nlocksdown[locktype] >= 0);
3256 assert(lockvar->nlocksup[locktype] >= 0);
3257
3258 for( v = lockvar->data.multaggr.nvars - 1; v >= 0; --v )
3259 {
3260 if( lockvar->data.multaggr.scalars[v] > 0.0 )
3261 {
3262 SCIP_CALL( SCIPvarAddLocks(lockvar->data.multaggr.vars[v], blkmem, set, eventqueue, locktype, addnlocksdown,
3263 addnlocksup) );
3264 }
3265 else
3266 {
3267 SCIP_CALL( SCIPvarAddLocks(lockvar->data.multaggr.vars[v], blkmem, set, eventqueue, locktype, addnlocksup,
3268 addnlocksdown) );
3269 }
3270 }
3271 return SCIP_OKAY;
3272 }
3273 case SCIP_VARSTATUS_NEGATED:
3274 {
3275 int tmp = addnlocksup;
3276
3277 assert(lockvar->negatedvar != NULL);
3278 assert(SCIPvarGetStatus(lockvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
3279 assert(lockvar->negatedvar->negatedvar == lockvar);
3280
3281 addnlocksup = addnlocksdown;
3282 addnlocksdown = tmp;
3283
3284 lockvar = lockvar->negatedvar;
3285 break;
3286 }
3287 default:
3288 SCIPerrorMessage("unknown variable status\n");
3289 return SCIP_INVALIDDATA;
3290 }
3291 }
3292 }
3293
3294 /** gets number of locks for rounding down of a special type */
3295 int SCIPvarGetNLocksDownType(
3296 SCIP_VAR* var, /**< problem variable */
3297 SCIP_LOCKTYPE locktype /**< type of variable locks */
3298 )
3299 {
3300 int nlocks;
3301 int i;
3302
3303 assert(var != NULL);
3304 assert((int)locktype >= 0 && (int)locktype < (int)NLOCKTYPES); /*lint !e685 !e568 !e587 !e650*/
3305 assert(var->nlocksdown[locktype] >= 0);
3306
3307 switch( SCIPvarGetStatus(var) )
3308 {
3309 case SCIP_VARSTATUS_ORIGINAL:
3310 if( var->data.original.transvar != NULL )
3311 return SCIPvarGetNLocksDownType(var->data.original.transvar, locktype);
3312 else
3313 return var->nlocksdown[locktype];
3314
3315 case SCIP_VARSTATUS_LOOSE:
3316 case SCIP_VARSTATUS_COLUMN:
3317 case SCIP_VARSTATUS_FIXED:
3318 return var->nlocksdown[locktype];
3319
3320 case SCIP_VARSTATUS_AGGREGATED:
3321 assert(!var->donotaggr);
3322 if( var->data.aggregate.scalar > 0.0 )
3323 return SCIPvarGetNLocksDownType(var->data.aggregate.var, locktype);
3324 else
3325 return SCIPvarGetNLocksUpType(var->data.aggregate.var, locktype);
3326
3327 case SCIP_VARSTATUS_MULTAGGR:
3328 assert(!var->donotmultaggr);
3329 nlocks = 0;
3330 for( i = 0; i < var->data.multaggr.nvars; ++i )
3331 {
3332 if( var->data.multaggr.scalars[i] > 0.0 )
3333 nlocks += SCIPvarGetNLocksDownType(var->data.multaggr.vars[i], locktype);
3334 else
3335 nlocks += SCIPvarGetNLocksUpType(var->data.multaggr.vars[i], locktype);
3336 }
3337 return nlocks;
3338
3339 case SCIP_VARSTATUS_NEGATED:
3340 assert(var->negatedvar != NULL);
3341 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
3342 assert(var->negatedvar->negatedvar == var);
3343 return SCIPvarGetNLocksUpType(var->negatedvar, locktype);
3344
3345 default:
3346 SCIPerrorMessage("unknown variable status\n");
3347 SCIPABORT();
3348 return INT_MAX; /*lint !e527*/
3349 }
3350 }
3351
3352 /** gets number of locks for rounding up of a special type */
3353 int SCIPvarGetNLocksUpType(
3354 SCIP_VAR* var, /**< problem variable */
3355 SCIP_LOCKTYPE locktype /**< type of variable locks */
3356 )
3357 {
3358 int nlocks;
3359 int i;
3360
3361 assert(var != NULL);
3362 assert((int)locktype >= 0 && (int)locktype < (int)NLOCKTYPES); /*lint !e685 !e568 !e587 !e650*/
3363 assert(var->nlocksup[locktype] >= 0);
3364
3365 switch( SCIPvarGetStatus(var) )
3366 {
3367 case SCIP_VARSTATUS_ORIGINAL:
3368 if( var->data.original.transvar != NULL )
3369 return SCIPvarGetNLocksUpType(var->data.original.transvar, locktype);
3370 else
3371 return var->nlocksup[locktype];
3372
3373 case SCIP_VARSTATUS_LOOSE:
3374 case SCIP_VARSTATUS_COLUMN:
3375 case SCIP_VARSTATUS_FIXED:
3376 return var->nlocksup[locktype];
3377
3378 case SCIP_VARSTATUS_AGGREGATED:
3379 assert(!var->donotaggr);
3380 if( var->data.aggregate.scalar > 0.0 )
3381 return SCIPvarGetNLocksUpType(var->data.aggregate.var, locktype);
3382 else
3383 return SCIPvarGetNLocksDownType(var->data.aggregate.var, locktype);
3384
3385 case SCIP_VARSTATUS_MULTAGGR:
3386 assert(!var->donotmultaggr);
3387 nlocks = 0;
3388 for( i = 0; i < var->data.multaggr.nvars; ++i )
3389 {
3390 if( var->data.multaggr.scalars[i] > 0.0 )
3391 nlocks += SCIPvarGetNLocksUpType(var->data.multaggr.vars[i], locktype);
3392 else
3393 nlocks += SCIPvarGetNLocksDownType(var->data.multaggr.vars[i], locktype);
3394 }
3395 return nlocks;
3396
3397 case SCIP_VARSTATUS_NEGATED:
3398 assert(var->negatedvar != NULL);
3399 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
3400 assert(var->negatedvar->negatedvar == var);
3401 return SCIPvarGetNLocksDownType(var->negatedvar, locktype);
3402
3403 default:
3404 SCIPerrorMessage("unknown variable status\n");
3405 SCIPABORT();
3406 return INT_MAX; /*lint !e527*/
3407 }
3408 }
3409
3410 /** gets number of locks for rounding down
3411 *
3412 * @note This method will always return variable locks of type model
3413 *
3414 * @note It is recommented to use SCIPvarGetNLocksDownType()
3415 */
3416 int SCIPvarGetNLocksDown(
3417 SCIP_VAR* var /**< problem variable */
3418 )
3419 {
3420 return SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL);
3421 }
3422
3423 /** gets number of locks for rounding up
3424 *
3425 * @note This method will always return variable locks of type model
3426 *
3427 * @note It is recommented to use SCIPvarGetNLocksUpType()
3428 */
3429 int SCIPvarGetNLocksUp(
3430 SCIP_VAR* var /**< problem variable */
3431 )
3432 {
3433 return SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL);
3434 }
3435
3436 /** is it possible, to round variable down and stay feasible?
3437 *
3438 * @note This method will always check w.r.t variable locks of type model
3439 */
3440 SCIP_Bool SCIPvarMayRoundDown(
3441 SCIP_VAR* var /**< problem variable */
3442 )
3443 {
3444 return (SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) == 0);
3445 }
3446
3447 /** is it possible, to round variable up and stay feasible?
3448 *
3449 * @note This method will always check w.r.t. variable locks of type model
3450 */
3451 SCIP_Bool SCIPvarMayRoundUp(
3452 SCIP_VAR* var /**< problem variable */
3453 )
3454 {
3455 return (SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) == 0);
3456 }
3457
3458 /** gets and captures transformed variable of a given variable; if the variable is not yet transformed,
3459 * a new transformed variable for this variable is created
3460 */
3461 SCIP_RETCODE SCIPvarTransform(
3462 SCIP_VAR* origvar, /**< original problem variable */
3463 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
3464 SCIP_SET* set, /**< global SCIP settings */
3465 SCIP_STAT* stat, /**< problem statistics */
3466 SCIP_OBJSENSE objsense, /**< objective sense of original problem; transformed is always MINIMIZE */
3467 SCIP_VAR** transvar /**< pointer to store the transformed variable */
3468 )
3469 {
3470 char name[SCIP_MAXSTRLEN];
3471
3472 assert(origvar != NULL);
3473 assert(origvar->scip == set->scip);
3474 assert(SCIPvarGetStatus(origvar) == SCIP_VARSTATUS_ORIGINAL);
3475 assert(SCIPsetIsEQ(set, origvar->glbdom.lb, origvar->locdom.lb));
3476 assert(SCIPsetIsEQ(set, origvar->glbdom.ub, origvar->locdom.ub));
3477 assert(origvar->vlbs == NULL);
3478 assert(origvar->vubs == NULL);
3479 assert(transvar != NULL);
3480
3481 /* check if variable is already transformed */
3482 if( origvar->data.original.transvar != NULL )
3483 {
3484 *transvar = origvar->data.original.transvar;
3485 SCIPvarCapture(*transvar);
3486 }
3487 else
3488 {
3489 int i;
3490
3491 /* create transformed variable */
3492 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "t_%s", origvar->name);
3493 SCIP_CALL( SCIPvarCreateTransformed(transvar, blkmem, set, stat, name,
3494 origvar->glbdom.lb, origvar->glbdom.ub, (SCIP_Real)objsense * origvar->obj,
3495 SCIPvarGetType(origvar), origvar->initial, origvar->removable,
3496 origvar->vardelorig, origvar->vartrans, origvar->vardeltrans, origvar->varcopy, NULL) );
3497
3498 /* copy the branch factor and priority */
3499 (*transvar)->branchfactor = origvar->branchfactor;
3500 (*transvar)->branchpriority = origvar->branchpriority;
3501 (*transvar)->branchdirection = origvar->branchdirection; /*lint !e732*/
3502
3503 /* duplicate hole lists */
3504 SCIP_CALL( holelistDuplicate(&(*transvar)->glbdom.holelist, blkmem, set, origvar->glbdom.holelist) );
3505 SCIP_CALL( holelistDuplicate(&(*transvar)->locdom.holelist, blkmem, set, origvar->locdom.holelist) );
3506
3507 /* link original and transformed variable */
3508 origvar->data.original.transvar = *transvar;
3509 SCIP_CALL( varAddParent(*transvar, blkmem, set, origvar) );
3510
3511 /* copy rounding locks */
3512 for( i = 0; i < NLOCKTYPES; i++ )
3513 {
3514 (*transvar)->nlocksdown[i] = origvar->nlocksdown[i];
3515 (*transvar)->nlocksup[i] = origvar->nlocksup[i];
3516 assert((*transvar)->nlocksdown[i] >= 0);
3517 assert((*transvar)->nlocksup[i] >= 0);
3518 }
3519
3520 /* copy donot(mult)aggr status */
3521 (*transvar)->donotaggr = origvar->donotaggr;
3522 (*transvar)->donotmultaggr = origvar->donotmultaggr;
3523
3524 /* copy lazy bounds */
3525 (*transvar)->lazylb = origvar->lazylb;
3526 (*transvar)->lazyub = origvar->lazyub;
3527
3528 /* transfer eventual variable statistics; do not update global statistics, because this has been done
3529 * when original variable was created
3530 */
3531 SCIPhistoryUnite((*transvar)->history, origvar->history, FALSE);
3532
3533 /* transform user data */
3534 if( origvar->vartrans != NULL )
3535 {
3536 SCIP_CALL( origvar->vartrans(set->scip, origvar, origvar->vardata, *transvar, &(*transvar)->vardata) );
3537 }
3538 else
3539 (*transvar)->vardata = origvar->vardata;
3540 }
3541
3542 SCIPsetDebugMsg(set, "transformed variable: <%s>[%p] -> <%s>[%p]\n", origvar->name, (void*)origvar, (*transvar)->name, (void*)*transvar);
3543
3544 return SCIP_OKAY;
3545 }
3546
3547 /** gets corresponding transformed variable of an original or negated original variable */
3548 SCIP_RETCODE SCIPvarGetTransformed(
3549 SCIP_VAR* origvar, /**< original problem variable */
3550 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
3551 SCIP_SET* set, /**< global SCIP settings */
3552 SCIP_STAT* stat, /**< problem statistics */
3553 SCIP_VAR** transvar /**< pointer to store the transformed variable, or NULL if not existing yet */
3554 )
3555 {
3556 assert(origvar != NULL);
3557 assert(SCIPvarGetStatus(origvar) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(origvar) == SCIP_VARSTATUS_NEGATED);
3558 assert(origvar->scip == set->scip);
3559
3560 if( SCIPvarGetStatus(origvar) == SCIP_VARSTATUS_NEGATED )
3561 {
3562 assert(origvar->negatedvar != NULL);
3563 assert(SCIPvarGetStatus(origvar->negatedvar) == SCIP_VARSTATUS_ORIGINAL);
3564
3565 if( origvar->negatedvar->data.original.transvar == NULL )
3566 *transvar = NULL;
3567 else
3568 {
3569 SCIP_CALL( SCIPvarNegate(origvar->negatedvar->data.original.transvar, blkmem, set, stat, transvar) );
3570 }
3571 }
3572 else
3573 *transvar = origvar->data.original.transvar;
3574
3575 return SCIP_OKAY;
3576 }
3577
3578 /** converts loose transformed variable into column variable, creates LP column */
3579 SCIP_RETCODE SCIPvarColumn(
3580 SCIP_VAR* var, /**< problem variable */
3581 BMS_BLKMEM* blkmem, /**< block memory */
3582 SCIP_SET* set, /**< global SCIP settings */
3583 SCIP_STAT* stat, /**< problem statistics */
3584 SCIP_PROB* prob, /**< problem data */
3585 SCIP_LP* lp /**< current LP data */
3586 )
3587 {
3588 assert(var != NULL);
3589 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
3590 assert(var->scip == set->scip);
3591
3592 SCIPsetDebugMsg(set, "creating column for variable <%s>\n", var->name);
3593
3594 /* switch variable status */
3595 var->varstatus = SCIP_VARSTATUS_COLUMN; /*lint !e641*/
3596
3597 /* create column of variable */
3598 SCIP_CALL( SCIPcolCreate(&var->data.col, blkmem, set, stat, var, 0, NULL, NULL, var->removable) );
3599
3600 if( var->probindex != -1 )
3601 {
3602 /* inform problem about the variable's status change */
3603 SCIP_CALL( SCIPprobVarChangedStatus(prob, blkmem, set, NULL, NULL, var) );
3604
3605 /* inform LP, that problem variable is now a column variable and no longer loose */
3606 SCIP_CALL( SCIPlpUpdateVarColumn(lp, set, var) );
3607 }
3608
3609 return SCIP_OKAY;
3610 }
3611
3612 /** converts column transformed variable back into loose variable, frees LP column */
3613 SCIP_RETCODE SCIPvarLoose(
3614 SCIP_VAR* var, /**< problem variable */
3615 BMS_BLKMEM* blkmem, /**< block memory */
3616 SCIP_SET* set, /**< global SCIP settings */
3617 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
3618 SCIP_PROB* prob, /**< problem data */
3619 SCIP_LP* lp /**< current LP data */
3620 )
3621 {
3622 assert(var != NULL);
3623 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
3624 assert(var->scip == set->scip);
3625 assert(var->data.col != NULL);
3626 assert(var->data.col->lppos == -1);
3627 assert(var->data.col->lpipos == -1);
3628
3629 SCIPsetDebugMsg(set, "deleting column for variable <%s>\n", var->name);
3630
3631 /* free column of variable */
3632 SCIP_CALL( SCIPcolFree(&var->data.col, blkmem, set, eventqueue, lp) );
3633
3634 /* switch variable status */
3635 var->varstatus = SCIP_VARSTATUS_LOOSE; /*lint !e641*/
3636
3637 if( var->probindex != -1 )
3638 {
3639 /* inform problem about the variable's status change */
3640 SCIP_CALL( SCIPprobVarChangedStatus(prob, blkmem, set, NULL, NULL, var) );
3641
3642 /* inform LP, that problem variable is now a loose variable and no longer a column */
3643 SCIP_CALL( SCIPlpUpdateVarLoose(lp, set, var) );
3644 }
3645
3646 return SCIP_OKAY;
3647 }
3648
3649 /** issues a VARFIXED event on the given variable and all its parents (except ORIGINAL parents);
3650 * the event issuing on the parents is necessary, because unlike with bound changes, the parent variables
3651 * are not informed about a fixing of an active variable they are pointing to
3652 */
3653 static
3654 SCIP_RETCODE varEventVarFixed(
3655 SCIP_VAR* var, /**< problem variable to change */
3656 BMS_BLKMEM* blkmem, /**< block memory */
3657 SCIP_SET* set, /**< global SCIP settings */
3658 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
3659 int fixeventtype /**< is this event a fixation(0), an aggregation(1), or a
3660 * multi-aggregation(2)
3661 */
3662 )
3663 {
3664 SCIP_EVENT* event;
3665 SCIP_VARSTATUS varstatus;
3666 int i;
3667
3668 assert(var != NULL);
3669 assert(var->scip == set->scip);
3670 assert(0 <= fixeventtype && fixeventtype <= 2);
3671
3672 /* issue VARFIXED event on variable */
3673 SCIP_CALL( SCIPeventCreateVarFixed(&event, blkmem, var) );
3674 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, NULL, &event) );
3675
3676 #ifndef NDEBUG
3677 for( i = var->nparentvars -1; i >= 0; --i )
3678 {
3679 assert(SCIPvarGetStatus(var->parentvars[i]) != SCIP_VARSTATUS_MULTAGGR);
3680 }
3681 #endif
3682
3683 switch( fixeventtype )
3684 {
3685 case 0:
3686 /* process all parents of a fixed variable */
3687 for( i = var->nparentvars - 1; i >= 0; --i )
3688 {
3689 varstatus = SCIPvarGetStatus(var->parentvars[i]);
3690
3691 assert(varstatus != SCIP_VARSTATUS_FIXED);
3692
3693 /* issue event on all not yet fixed parent variables, (that should already issued this event) except the original
3694 * one
3695 */
3696 if( varstatus != SCIP_VARSTATUS_ORIGINAL )
3697 {
3698 SCIP_CALL( varEventVarFixed(var->parentvars[i], blkmem, set, eventqueue, fixeventtype) );
3699 }
3700 }
3701 break;
3702 case 1:
3703 /* process all parents of a aggregated variable */
3704 for( i = var->nparentvars - 1; i >= 0; --i )
3705 {
3706 varstatus = SCIPvarGetStatus(var->parentvars[i]);
3707
3708 assert(varstatus != SCIP_VARSTATUS_FIXED);
3709
3710 /* issue event for not aggregated parent variable, because for these and its parents the var event was already
3711 * issued(, except the original one)
3712 *
3713 * @note that even before an aggregated parent variable, there might be variables, for which the vent was not
3714 * yet issued
3715 */
3716 if( varstatus == SCIP_VARSTATUS_AGGREGATED )
3717 continue;
3718
3719 if( varstatus != SCIP_VARSTATUS_ORIGINAL )
3720 {
3721 SCIP_CALL( varEventVarFixed(var->parentvars[i], blkmem, set, eventqueue, fixeventtype) );
3722 }
3723 }
3724 break;
3725 case 2:
3726 /* process all parents of a aggregated variable */
3727 for( i = var->nparentvars - 1; i >= 0; --i )
3728 {
3729 varstatus = SCIPvarGetStatus(var->parentvars[i]);
3730
3731 assert(varstatus != SCIP_VARSTATUS_FIXED);
3732
3733 /* issue event on all parent variables except the original one */
3734 if( varstatus != SCIP_VARSTATUS_ORIGINAL )
3735 {
3736 SCIP_CALL( varEventVarFixed(var->parentvars[i], blkmem, set, eventqueue, fixeventtype) );
3737 }
3738 }
3739 break;
3740 default:
3741 SCIPerrorMessage("unknown variable fixation event origin\n");
3742 return SCIP_INVALIDDATA;
3743 }
3744
3745 return SCIP_OKAY;
3746 }
3747
3748 /** converts variable into fixed variable */
3749 SCIP_RETCODE SCIPvarFix(
3750 SCIP_VAR* var, /**< problem variable */
3751 BMS_BLKMEM* blkmem, /**< block memory */
3752 SCIP_SET* set, /**< global SCIP settings */
3753 SCIP_STAT* stat, /**< problem statistics */
3754 SCIP_PROB* transprob, /**< tranformed problem data */
3755 SCIP_PROB* origprob, /**< original problem data */
3756 SCIP_PRIMAL* primal, /**< primal data */
3757 SCIP_TREE* tree, /**< branch and bound tree */
3758 SCIP_REOPT* reopt, /**< reoptimization data structure */
3759 SCIP_LP* lp, /**< current LP data */
3760 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
3761 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
3762 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
3763 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
3764 SCIP_Real fixedval, /**< value to fix variable at */
3765 SCIP_Bool* infeasible, /**< pointer to store whether the fixing is infeasible */
3766 SCIP_Bool* fixed /**< pointer to store whether the fixing was performed (variable was unfixed) */
3767 )
3768 {
3769 SCIP_Real obj;
3770 SCIP_Real childfixedval;
3771
3772 assert(var != NULL);
3773 assert(var->scip == set->scip);
3774 assert(SCIPsetIsEQ(set, var->glbdom.lb, var->locdom.lb));
3775 assert(SCIPsetIsEQ(set, var->glbdom.ub, var->locdom.ub));
3776 assert(infeasible != NULL);
3777 assert(fixed != NULL);
3778
3779 SCIPsetDebugMsg(set, "fix variable <%s>[%g,%g] to %g\n", var->name, var->glbdom.lb, var->glbdom.ub, fixedval);
3780
3781 *infeasible = FALSE;
3782 *fixed = FALSE;
3783
3784 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED )
3785 {
3786 *infeasible = !SCIPsetIsFeasEQ(set, fixedval, var->locdom.lb);
3787 SCIPsetDebugMsg(set, " -> variable already fixed to %g (fixedval=%g): infeasible=%u\n", var->locdom.lb, fixedval, *infeasible);
3788 return SCIP_OKAY;
3789 }
3790 else if( (SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS && !SCIPsetIsFeasIntegral(set, fixedval))
3791 || SCIPsetIsFeasLT(set, fixedval, var->locdom.lb)
3792 || SCIPsetIsFeasGT(set, fixedval, var->locdom.ub) )
3793 {
3794 SCIPsetDebugMsg(set, " -> fixing infeasible: locdom=[%g,%g], fixedval=%g\n", var->locdom.lb, var->locdom.ub, fixedval);
3795 *infeasible = TRUE;
3796 return SCIP_OKAY;
3797 }
3798
3799 switch( SCIPvarGetStatus(var) )
3800 {
3801 case SCIP_VARSTATUS_ORIGINAL:
3802 if( var->data.original.transvar == NULL )
3803 {
3804 SCIPerrorMessage("cannot fix an untransformed original variable\n");
3805 return SCIP_INVALIDDATA;
3806 }
3807 SCIP_CALL( SCIPvarFix(var->data.original.transvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt,
3808 lp, branchcand, eventfilter, eventqueue, cliquetable, fixedval, infeasible, fixed) );
3809 break;
3810
3811 case SCIP_VARSTATUS_LOOSE:
3812 assert(!SCIPeventqueueIsDelayed(eventqueue)); /* otherwise, the pseudo objective value update gets confused */
3813
3814 /* set the fixed variable's objective value to 0.0 */
3815 obj = var->obj;
3816 SCIP_CALL( SCIPvarChgObj(var, blkmem, set, transprob, primal, lp, eventqueue, 0.0) );
3817
3818 /* since we change the variable type form loose to fixed, we have to adjust the number of loose
3819 * variables in the LP data structure; the loose objective value (looseobjval) in the LP data structure, however,
3820 * gets adjusted automatically, due to the event SCIP_EVENTTYPE_OBJCHANGED which dropped in the moment where the
3821 * objective of this variable is set to zero
3822 */
3823 SCIPlpDecNLoosevars(lp);
3824
3825 /* change variable's bounds to fixed value (thereby removing redundant implications and variable bounds) */
3826 holelistFree(&var->glbdom.holelist, blkmem);
3827 holelistFree(&var->locdom.holelist, blkmem);
3828 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, fixedval) );
3829 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, fixedval) );
3830
3831 if( var->glbdom.lb != var->glbdom.ub ) /*lint !e777*/
3832 {
3833 /* explicitly set variable's bounds if the fixed value was in epsilon range of the old bound (so above call didn't set bound) */
3834 if( SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS )
3835 {
3836 /* if not continuous variable, then make sure variable is fixed to integer value */
3837 assert(SCIPsetIsIntegral(set, fixedval));
3838 fixedval = SCIPsetRound(set, fixedval);
3839 }
3840 var->glbdom.lb = fixedval;
3841 var->glbdom.ub = fixedval;
3842 }
3843
3844 /* ensure local domain is fixed to same value as global domain */
3845 var->locdom.lb = var->glbdom.lb;
3846 var->locdom.ub = var->glbdom.ub;
3847
3848 /* delete implications and variable bounds information */
3849 SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, TRUE) );
3850 assert(var->vlbs == NULL);
3851 assert(var->vubs == NULL);
3852 assert(var->implics == NULL);
3853
3854 /* clear the history of the variable */
3855 SCIPhistoryReset(var->history);
3856 SCIPhistoryReset(var->historycrun);
3857
3858 /* convert variable into fixed variable */
3859 var->varstatus = SCIP_VARSTATUS_FIXED; /*lint !e641*/
3860
3861 /* inform problem about the variable's status change */
3862 if( var->probindex != -1 )
3863 {
3864 SCIP_CALL( SCIPprobVarChangedStatus(transprob, blkmem, set, branchcand, cliquetable, var) );
3865 }
3866
3867 /* reset the objective value of the fixed variable, thus adjusting the problem's objective offset */
3868 SCIP_CALL( SCIPvarAddObj(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, eventfilter, eventqueue, obj) );
3869
3870 /* issue VARFIXED event */
3871 SCIP_CALL( varEventVarFixed(var, blkmem, set, eventqueue, 0) );
3872
3873 *fixed = TRUE;
3874 break;
3875
3876 case SCIP_VARSTATUS_COLUMN:
3877 SCIPerrorMessage("cannot fix a column variable\n");
3878 return SCIP_INVALIDDATA;
3879
3880 case SCIP_VARSTATUS_FIXED:
3881 SCIPerrorMessage("cannot fix a fixed variable again\n"); /*lint !e527*/
3882 SCIPABORT(); /* case is already handled in earlier if condition */
3883 return SCIP_INVALIDDATA; /*lint !e527*/
3884
3885 case SCIP_VARSTATUS_AGGREGATED:
3886 /* fix aggregation variable y in x = a*y + c, instead of fixing x directly */
3887 assert(SCIPsetIsZero(set, var->obj));
3888 assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
3889 if( SCIPsetIsInfinity(set, fixedval) || SCIPsetIsInfinity(set, -fixedval) )
3890 childfixedval = (var->data.aggregate.scalar < 0.0 ? -fixedval : fixedval);
3891 else
3892 childfixedval = (fixedval - var->data.aggregate.constant)/var->data.aggregate.scalar;
3893 SCIP_CALL( SCIPvarFix(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
3894 branchcand, eventfilter, eventqueue, cliquetable, childfixedval, infeasible, fixed) );
3895 break;
3896
3897 case SCIP_VARSTATUS_MULTAGGR:
3898 SCIPerrorMessage("cannot fix a multiple aggregated variable\n");
3899 SCIPABORT();
3900 return SCIP_INVALIDDATA; /*lint !e527*/
3901
3902 case SCIP_VARSTATUS_NEGATED:
3903 /* fix negation variable x in x' = offset - x, instead of fixing x' directly */
3904 assert(SCIPsetIsZero(set, var->obj));
3905 assert(var->negatedvar != NULL);
3906 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
3907 assert(var->negatedvar->negatedvar == var);
3908 SCIP_CALL( SCIPvarFix(var->negatedvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
3909 branchcand, eventfilter, eventqueue, cliquetable, var->data.negate.constant - fixedval, infeasible, fixed) );
3910 break;
3911
3912 default:
3913 SCIPerrorMessage("unknown variable status\n");
3914 return SCIP_INVALIDDATA;
3915 }
3916
3917 return SCIP_OKAY;
3918 }
3919
3920 /** transforms given variables, scalars and constant to the corresponding active variables, scalars and constant
3921 *
3922 * If the number of needed active variables is greater than the available slots in the variable array, nothing happens except
3923 * that the required size is stored in the corresponding variable; hence, if afterwards the required size is greater than the
3924 * available slots (varssize), nothing happens; otherwise, the active variable representation is stored in the arrays.
3925 *
3926 * The reason for this approach is that we cannot reallocate memory, since we do not know how the
3927 * memory has been allocated (e.g., by a C++ 'new' or SCIP functions).
3928 */
3929 SCIP_RETCODE SCIPvarGetActiveRepresentatives(
3930 SCIP_SET* set, /**< global SCIP settings */
3931 SCIP_VAR** vars, /**< variable array to get active variables */
3932 SCIP_Real* scalars, /**< scalars a_1, ..., a_n in linear sum a_1*x_1 + ... + a_n*x_n + c */
3933 int* nvars, /**< pointer to number of variables and values in vars and scalars array */
3934 int varssize, /**< available slots in vars and scalars array */
3935 SCIP_Real* constant, /**< pointer to constant c in linear sum a_1*x_1 + ... + a_n*x_n + c */
3936 int* requiredsize, /**< pointer to store the required array size for the active variables */
3937 SCIP_Bool mergemultiples /**< should multiple occurrences of a var be replaced by a single coeff? */
3938 )
3939 {
3940 SCIP_VAR** activevars;
3941 SCIP_Real* activescalars;
3942 int nactivevars;
3943 SCIP_Real activeconstant;
3944 SCIP_Bool activeconstantinf;
3945 int activevarssize;
3946
3947 SCIP_VAR* var;
3948 SCIP_Real scalar;
3949 int v;
3950 int k;
3951
3952 SCIP_VAR** tmpvars;
3953 SCIP_VAR** multvars;
3954 SCIP_Real* tmpscalars;
3955 SCIP_Real* multscalars;
3956 int tmpvarssize;
3957 int ntmpvars;
3958 int nmultvars;
3959
3960 SCIP_VAR* multvar;
3961 SCIP_Real multscalar;
3962 SCIP_Real multconstant;
3963 int pos;
3964
3965 int noldtmpvars;
3966
3967 SCIP_VAR** tmpvars2;
3968 SCIP_Real* tmpscalars2;
3969 int tmpvarssize2;
3970 int ntmpvars2;
3971
3972 SCIP_Bool sortagain = FALSE;
3973
3974 assert(set != NULL);
3975 assert(nvars != NULL);
3976 assert(scalars != NULL || *nvars == 0);
3977 assert(constant != NULL);
3978 assert(requiredsize != NULL);
3979 assert(*nvars <= varssize);
3980
3981 *requiredsize = 0;
3982
(1) Event cond_false: |
Condition "*nvars == 0", taking false branch. |
3983 if( *nvars == 0 )
(2) Event if_end: |
End of if statement. |
3984 return SCIP_OKAY;
3985
3986 assert(vars != NULL);
3987
3988 /* handle the "easy" case of just one variable and avoid memory allocation if the variable is already active */
(3) Event cond_true: |
Condition "*nvars == 1", taking true branch. |
(4) Event deref_parm: |
Directly dereferencing parameter "vars". |
3989 if( *nvars == 1 && (vars[0]->varstatus == ((int) SCIP_VARSTATUS_COLUMN) || vars[0]->varstatus == ((int) SCIP_VARSTATUS_LOOSE)) )
3990 {
3991 *requiredsize = 1;
3992
3993 return SCIP_OKAY;
3994 }
3995
3996 nactivevars = 0;
3997 activeconstant = 0.0;
3998 activeconstantinf = FALSE;
3999 activevarssize = (*nvars) * 2;
4000 ntmpvars = *nvars;
4001 tmpvarssize = *nvars;
4002
4003 tmpvarssize2 = 1;
4004
4005 /* allocate temporary memory */
4006 SCIP_CALL( SCIPsetAllocBufferArray(set, &tmpvars2, tmpvarssize2) );
4007 SCIP_CALL( SCIPsetAllocBufferArray(set, &tmpscalars2, tmpvarssize2) );
4008 SCIP_CALL( SCIPsetAllocBufferArray(set, &activevars, activevarssize) );
4009 SCIP_CALL( SCIPsetAllocBufferArray(set, &activescalars, activevarssize) );
4010 SCIP_CALL( SCIPsetDuplicateBufferArray(set, &tmpvars, vars, ntmpvars) );
4011 SCIP_CALL( SCIPsetDuplicateBufferArray(set, &tmpscalars, scalars, ntmpvars) );
4012
4013 /* to avoid unnecessary expanding of variable arrays while disaggregating several variables multiple times combine same variables
4014 * first, first get all corresponding variables with status loose, column, multaggr or fixed
4015 */
4016 for( v = ntmpvars - 1; v >= 0; --v )
4017 {
4018 var = tmpvars[v];
4019 scalar = tmpscalars[v];
4020
4021 assert(var != NULL);
4022 /* transforms given variable, scalar and constant to the corresponding active, fixed, or
4023 * multi-aggregated variable, scalar and constant; if the variable resolves to a fixed
4024 * variable, "scalar" will be 0.0 and the value of the sum will be stored in "constant".
4025 */
4026 SCIP_CALL( SCIPvarGetProbvarSum(&var, set, &scalar, &activeconstant) );
4027 assert(var != NULL);
4028
4029 assert(SCIPsetIsInfinity(set, activeconstant) == (activeconstant == SCIPsetInfinity(set))); /*lint !e777*/
4030 assert(SCIPsetIsInfinity(set, -activeconstant) == (activeconstant == -SCIPsetInfinity(set))); /*lint !e777*/
4031
4032 activeconstantinf = SCIPsetIsInfinity(set, activeconstant) || SCIPsetIsInfinity(set, -activeconstant);
4033
4034 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
4035 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
4036 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR
4037 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED);
4038
4039 tmpvars[v] = var;
4040 tmpscalars[v] = scalar;
4041 }
4042 noldtmpvars = ntmpvars;
4043
4044 /* sort all variables to combine equal variables easily */
4045 SCIPsortPtrReal((void**)tmpvars, tmpscalars, SCIPvarComp, noldtmpvars);
4046 ntmpvars = 0;
4047 for( v = 1; v < noldtmpvars; ++v )
4048 {
4049 /* combine same variables */
4050 if( SCIPvarCompare(tmpvars[v], tmpvars[ntmpvars]) == 0 )
4051 {
4052 tmpscalars[ntmpvars] += tmpscalars[v];
4053 }
4054 else
4055 {
4056 ++ntmpvars;
4057 if( v > ntmpvars )
4058 {
4059 tmpscalars[ntmpvars] = tmpscalars[v];
4060 tmpvars[ntmpvars] = tmpvars[v];
4061 }
4062 }
4063 }
4064 ++ntmpvars;
4065
4066 #ifdef SCIP_MORE_DEBUG
4067 for( v = 1; v < ntmpvars; ++v )
4068 assert(SCIPvarCompare(tmpvars[v], tmpvars[v-1]) > 0);
4069 #endif
4070
4071 /* collect for each variable the representation in active variables */
4072 while( ntmpvars >= 1 )
4073 {
4074 --ntmpvars;
4075 ntmpvars2 = 0;
4076 var = tmpvars[ntmpvars];
4077 scalar = tmpscalars[ntmpvars];
4078
4079 assert(var != NULL);
4080
4081 /* TODO: maybe we should test here on SCIPsetIsZero() instead of 0.0 */
4082 if( scalar == 0.0 )
4083 continue;
4084
4085 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
4086 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
4087 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR
4088 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED);
4089
4090 switch( SCIPvarGetStatus(var) )
4091 {
4092 case SCIP_VARSTATUS_LOOSE:
4093 case SCIP_VARSTATUS_COLUMN:
4094 /* x = a*y + c */
4095 if( nactivevars >= activevarssize )
4096 {
4097 activevarssize *= 2;
4098 SCIP_CALL( SCIPsetReallocBufferArray(set, &activevars, activevarssize) );
4099 SCIP_CALL( SCIPsetReallocBufferArray(set, &activescalars, activevarssize) );
4100 assert(nactivevars < activevarssize);
4101 }
4102 activevars[nactivevars] = var;
4103 activescalars[nactivevars] = scalar;
4104 nactivevars++;
4105 break;
4106
4107 case SCIP_VARSTATUS_MULTAGGR:
4108 /* x = a_1*y_1 + ... + a_n*y_n + c */
4109 nmultvars = var->data.multaggr.nvars;
4110 multvars = var->data.multaggr.vars;
4111 multscalars = var->data.multaggr.scalars;
4112 sortagain = TRUE;
4113
4114 if( nmultvars + ntmpvars > tmpvarssize )
4115 {
4116 while( nmultvars + ntmpvars > tmpvarssize )
4117 tmpvarssize *= 2;
4118 SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpvars, tmpvarssize) );
4119 SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpscalars, tmpvarssize) );
4120 assert(nmultvars + ntmpvars <= tmpvarssize);
4121 }
4122
4123 if( nmultvars > tmpvarssize2 )
4124 {
4125 while( nmultvars > tmpvarssize2 )
4126 tmpvarssize2 *= 2;
4127 SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpvars2, tmpvarssize2) );
4128 SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpscalars2, tmpvarssize2) );
4129 assert(nmultvars <= tmpvarssize2);
4130 }
4131
4132 --nmultvars;
4133
4134 for( ; nmultvars >= 0; --nmultvars )
4135 {
4136 multvar = multvars[nmultvars];
4137 multscalar = multscalars[nmultvars];
4138 multconstant = 0;
4139
4140 assert(multvar != NULL);
4141 SCIP_CALL( SCIPvarGetProbvarSum(&multvar, set, &multscalar, &multconstant) );
4142 assert(multvar != NULL);
4143
4144 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
4145 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
4146 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR
4147 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED);
4148
4149 if( !activeconstantinf )
4150 {
4151 assert(!SCIPsetIsInfinity(set, scalar) && !SCIPsetIsInfinity(set, -scalar));
4152
4153 if( SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant) )
4154 {
4155 assert(scalar != 0.0);
4156 if( scalar * multconstant > 0.0 )
4157 {
4158 activeconstant = SCIPsetInfinity(set);
4159 activeconstantinf = TRUE;
4160 }
4161 else
4162 {
4163 activeconstant = -SCIPsetInfinity(set);
4164 activeconstantinf = TRUE;
4165 }
4166 }
4167 else
4168 activeconstant += scalar * multconstant;
4169 }
4170 #ifndef NDEBUG
4171 else
4172 {
4173 assert(!SCIPsetIsInfinity(set, activeconstant) || !(scalar * multconstant < 0.0 &&
4174 (SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant))));
4175 assert(!SCIPsetIsInfinity(set, -activeconstant) || !(scalar * multconstant > 0.0 &&
4176 (SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant))));
4177 }
4178 #endif
4179
4180 if( SCIPsortedvecFindPtr((void**)tmpvars, SCIPvarComp, multvar, ntmpvars, &pos) )
4181 {
4182 assert(SCIPvarCompare(tmpvars[pos], multvar) == 0);
4183 tmpscalars[pos] += scalar * multscalar;
4184 }
4185 else
4186 {
4187 tmpvars2[ntmpvars2] = multvar;
4188 tmpscalars2[ntmpvars2] = scalar * multscalar;
4189 ++(ntmpvars2);
4190 assert(ntmpvars2 <= tmpvarssize2);
4191 }
4192 }
4193
4194 if( ntmpvars2 > 0 )
4195 {
4196 /* sort all variables to combine equal variables easily */
4197 SCIPsortPtrReal((void**)tmpvars2, tmpscalars2, SCIPvarComp, ntmpvars2);
4198 pos = 0;
4199 for( v = 1; v < ntmpvars2; ++v )
4200 {
4201 /* combine same variables */
4202 if( SCIPvarCompare(tmpvars2[v], tmpvars2[pos]) == 0 )
4203 {
4204 tmpscalars2[pos] += tmpscalars2[v];
4205 }
4206 else
4207 {
4208 ++pos;
4209 if( v > pos )
4210 {
4211 tmpscalars2[pos] = tmpscalars2[v];
4212 tmpvars2[pos] = tmpvars2[v];
4213 }
4214 }
4215 }
4216 ntmpvars2 = pos + 1;
4217 #ifdef SCIP_MORE_DEBUG
4218 for( v = 1; v < ntmpvars2; ++v )
4219 {
4220 assert(SCIPvarCompare(tmpvars2[v], tmpvars2[v-1]) > 0);
4221 }
4222 for( v = 1; v < ntmpvars; ++v )
4223 {
4224 assert(SCIPvarCompare(tmpvars[v], tmpvars[v-1]) > 0);
4225 }
4226 #endif
4227 v = ntmpvars - 1;
4228 k = ntmpvars2 - 1;
4229 pos = ntmpvars + ntmpvars2 - 1;
4230 ntmpvars += ntmpvars2;
4231
4232 while( v >= 0 && k >= 0 )
4233 {
4234 assert(pos >= 0);
4235 assert(SCIPvarCompare(tmpvars[v], tmpvars2[k]) != 0);
4236 if( SCIPvarCompare(tmpvars[v], tmpvars2[k]) >= 0 )
4237 {
4238 tmpvars[pos] = tmpvars[v];
4239 tmpscalars[pos] = tmpscalars[v];
4240 --v;
4241 }
4242 else
4243 {
4244 tmpvars[pos] = tmpvars2[k];
4245 tmpscalars[pos] = tmpscalars2[k];
4246 --k;
4247 }
4248 --pos;
4249 assert(pos >= 0);
4250 }
4251 while( v >= 0 )
4252 {
4253 assert(pos >= 0);
4254 tmpvars[pos] = tmpvars[v];
4255 tmpscalars[pos] = tmpscalars[v];
4256 --v;
4257 --pos;
4258 }
4259 while( k >= 0 )
4260 {
4261 assert(pos >= 0);
4262 tmpvars[pos] = tmpvars2[k];
4263 tmpscalars[pos] = tmpscalars2[k];
4264 --k;
4265 --pos;
4266 }
4267 }
4268 #ifdef SCIP_MORE_DEBUG
4269 for( v = 1; v < ntmpvars; ++v )
4270 {
4271 assert(SCIPvarCompare(tmpvars[v], tmpvars[v-1]) > 0);
4272 }
4273 #endif
4274
4275 if( !activeconstantinf )
4276 {
4277 assert(!SCIPsetIsInfinity(set, scalar) && !SCIPsetIsInfinity(set, -scalar));
4278
4279 multconstant = SCIPvarGetMultaggrConstant(var);
4280
4281 if( SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant) )
4282 {
4283 assert(scalar != 0.0);
4284 if( scalar * multconstant > 0.0 )
4285 {
4286 activeconstant = SCIPsetInfinity(set);
4287 activeconstantinf = TRUE;
4288 }
4289 else
4290 {
4291 activeconstant = -SCIPsetInfinity(set);
4292 activeconstantinf = TRUE;
4293 }
4294 }
4295 else
4296 activeconstant += scalar * multconstant;
4297 }
4298 #ifndef NDEBUG
4299 else
4300 {
4301 multconstant = SCIPvarGetMultaggrConstant(var);
4302 assert(!SCIPsetIsInfinity(set, activeconstant) || !(scalar * multconstant < 0.0 &&
4303 (SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant))));
4304 assert(!SCIPsetIsInfinity(set, -activeconstant) || !(scalar * multconstant > 0.0 &&
4305 (SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant))));
4306 }
4307 #endif
4308 break;
4309
4310 case SCIP_VARSTATUS_FIXED:
4311 case SCIP_VARSTATUS_ORIGINAL:
4312 case SCIP_VARSTATUS_AGGREGATED:
4313 case SCIP_VARSTATUS_NEGATED:
4314 default:
4315 /* case x = c, but actually we should not be here, since SCIPvarGetProbvarSum() returns a scalar of 0.0 for
4316 * fixed variables and is handled already
4317 */
4318 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED);
4319 assert(SCIPsetIsZero(set, var->glbdom.lb) && SCIPsetIsEQ(set, var->glbdom.lb, var->glbdom.ub));
4320 }
4321 }
4322
4323 if( mergemultiples )
4324 {
4325 if( sortagain )
4326 {
4327 /* sort variable and scalar array by variable index */
4328 SCIPsortPtrReal((void**)activevars, activescalars, SCIPvarComp, nactivevars);
4329
4330 /* eliminate duplicates and count required size */
4331 v = nactivevars - 1;
4332 while( v > 0 )
4333 {
4334 /* combine both variable since they are the same */
4335 if( SCIPvarCompare(activevars[v - 1], activevars[v]) == 0 )
4336 {
4337 if( activescalars[v - 1] + activescalars[v] != 0.0 )
4338 {
4339 activescalars[v - 1] += activescalars[v];
4340 --nactivevars;
4341 activevars[v] = activevars[nactivevars];
4342 activescalars[v] = activescalars[nactivevars];
4343 }
4344 else
4345 {
4346 --nactivevars;
4347 activevars[v] = activevars[nactivevars];
4348 activescalars[v] = activescalars[nactivevars];
4349 --nactivevars;
4350 --v;
4351 activevars[v] = activevars[nactivevars];
4352 activescalars[v] = activescalars[nactivevars];
4353 }
4354 }
4355 --v;
4356 }
4357 }
4358 /* the variables were added in reverse order, we revert the order now;
4359 * this should not be necessary, but not doing this changes the behavior sometimes
4360 */
4361 else
4362 {
4363 SCIP_VAR* tmpvar;
4364 SCIP_Real tmpscalar;
4365
4366 for( v = 0; v < nactivevars / 2; ++v )
4367 {
4368 tmpvar = activevars[v];
4369 tmpscalar = activescalars[v];
4370 activevars[v] = activevars[nactivevars - 1 - v];
4371 activescalars[v] = activescalars[nactivevars - 1 - v];
4372 activevars[nactivevars - 1 - v] = tmpvar;
4373 activescalars[nactivevars - 1 - v] = tmpscalar;
4374 }
4375 }
4376 }
4377 *requiredsize = nactivevars;
4378
4379 if( varssize >= *requiredsize )
4380 {
4381 assert(vars != NULL);
4382
4383 *nvars = *requiredsize;
4384
4385 if( !SCIPsetIsInfinity(set, *constant) && !SCIPsetIsInfinity(set, -(*constant)) )
4386 {
4387 /* if the activeconstant is infinite, the constant pointer gets the same value, otherwise add the value */
4388 if( activeconstantinf )
4389 (*constant) = activeconstant;
4390 else
4391 (*constant) += activeconstant;
4392 }
4393 #ifndef NDEBUG
4394 else
4395 {
4396 assert(!SCIPsetIsInfinity(set, (*constant)) || !SCIPsetIsInfinity(set, -activeconstant));
4397 assert(!SCIPsetIsInfinity(set, -(*constant)) || !SCIPsetIsInfinity(set, activeconstant));
4398 }
4399 #endif
4400
4401 /* copy active variable and scalar array to the given arrays */
4402 for( v = 0; v < *nvars; ++v )
4403 {
4404 vars[v] = activevars[v];
4405 scalars[v] = activescalars[v]; /*lint !e613*/
4406 }
4407 }
4408
4409 assert(SCIPsetIsInfinity(set, *constant) == ((*constant) == SCIPsetInfinity(set))); /*lint !e777*/
4410 assert(SCIPsetIsInfinity(set, -(*constant)) == ((*constant) == -SCIPsetInfinity(set))); /*lint !e777*/
4411
4412 SCIPsetFreeBufferArray(set, &tmpscalars);
4413 SCIPsetFreeBufferArray(set, &tmpvars);
4414 SCIPsetFreeBufferArray(set, &activescalars);
4415 SCIPsetFreeBufferArray(set, &activevars);
4416 SCIPsetFreeBufferArray(set, &tmpscalars2);
4417 SCIPsetFreeBufferArray(set, &tmpvars2);
4418
4419 return SCIP_OKAY;
4420 }
4421
4422
4423 /** flattens aggregation graph of multi-aggregated variable in order to avoid exponential recursion later on */
4424 SCIP_RETCODE SCIPvarFlattenAggregationGraph(
4425 SCIP_VAR* var, /**< problem variable */
4426 BMS_BLKMEM* blkmem, /**< block memory */
4427 SCIP_SET* set, /**< global SCIP settings */
4428 SCIP_EVENTQUEUE* eventqueue /**< event queue */
4429 )
4430 {
4431 int nlocksup[NLOCKTYPES];
4432 int nlocksdown[NLOCKTYPES];
4433 SCIP_Real multconstant;
4434 int multvarssize;
4435 int nmultvars;
4436 int multrequiredsize;
4437 int i;
4438
4439 assert( var != NULL );
4440 assert( SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR );
4441 assert(var->scip == set->scip);
4442
4443 /* in order to update the locks on the active representation of the multi-aggregated variable, we remove all locks
4444 * on the current representation now and re-add the locks once the variable graph has been flattened, which
4445 * may lead to duplicate occurences of the same variable being merged
4446 *
4447 * Here is an example. Assume we have the multi-aggregation z = x + y.
4448 * z occures with positive coefficient in a <= constraint c1, so it has an uplock from there.
4449 * When the multi-aggregation is performed, all locks are added to the active representation,
4450 * so x and y both get an uplock from c1. However, z was not yet replaced by x + y in c1.
4451 * Next, a negation y = 1 - x is identified. Again, locks are moved, so that the uplock of y originating
4452 * from c1 is added to x as a downlock. Thus, x has both an up- and downlock from c1.
4453 * The multi-aggregation changes to z = x + 1 - x, which corresponds to the locks.
4454 * However, before z is replaced by that sum, SCIPvarFlattenAggregationGraph() is called
4455 * which changes z = x + y = x + 1 - x = 1, since it merges multiple occurences of the same variable.
4456 * The up- and downlock of x, however, is not removed when replacing z in c1 by its active representation,
4457 * because it is just 1 now. Therefore, we need to update locks when flattening the aggregation graph.
4458 * For this, the multi-aggregated variable knows its locks in addition to adding them to the active
4459 * representation, which corresponds to the locks from constraints where the variable was not replaced yet.
4460 * By removing the locks here, based on the old representation and adding them again after flattening,
4461 * we ensure that the locks are correct afterwards if coefficients were merged.
4462 */
4463 for( i = 0; i < NLOCKTYPES; ++i )
4464 {
4465 nlocksup[i] = var->nlocksup[i];
4466 nlocksdown[i] = var->nlocksdown[i];
4467
4468 SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, -nlocksdown[i], -nlocksup[i]) );
4469 }
4470
4471 multconstant = var->data.multaggr.constant;
4472 nmultvars = var->data.multaggr.nvars;
4473 multvarssize = var->data.multaggr.varssize;
4474
4475 SCIP_CALL( SCIPvarGetActiveRepresentatives(set, var->data.multaggr.vars, var->data.multaggr.scalars, &nmultvars, multvarssize, &multconstant, &multrequiredsize, TRUE) );
4476
4477 if( multrequiredsize > multvarssize )
4478 {
4479 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(var->data.multaggr.vars), multvarssize, multrequiredsize) );
4480 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(var->data.multaggr.scalars), multvarssize, multrequiredsize) );
4481 multvarssize = multrequiredsize;
4482 SCIP_CALL( SCIPvarGetActiveRepresentatives(set, var->data.multaggr.vars, var->data.multaggr.scalars, &nmultvars, multvarssize, &multconstant, &multrequiredsize, TRUE) );
4483 assert( multrequiredsize <= multvarssize );
4484 }
4485 /**@note After the flattening the multi aggregation might resolve to be in fact an aggregation (or even a fixing?).
4486 * This issue is not resolved right now, since var->data.multaggr.nvars < 2 should not cause troubles. However, one
4487 * may loose performance hereby, since aggregated variables are easier to handle.
4488 *
4489 * Note, that there are two cases where SCIPvarFlattenAggregationGraph() is called: The easier one is that it is
4490 * called while installing the multi-aggregation. in principle, the described issue could be handled straightforward
4491 * in this case by aggregating or fixing the variable instead. The more complicated case is the one, when the
4492 * multi-aggregation is used, e.g., in linear presolving (and the variable is already declared to be multi-aggregated).
4493 *
4494 * By now, it is not allowed to fix or aggregate multi-aggregated variables which would be necessary in this case.
4495 *
4496 * The same issue appears in the SCIPvarGetProbvar...() methods.
4497 */
4498
4499 var->data.multaggr.constant = multconstant;
4500 var->data.multaggr.nvars = nmultvars;
4501 var->data.multaggr.varssize = multvarssize;
4502
4503 for( i = 0; i < NLOCKTYPES; ++i )
4504 {
4505 SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, nlocksdown[i], nlocksup[i]) );
4506 }
4507
4508 return SCIP_OKAY;
4509 }
4510
4511 /** merge two variable histories together; a typical use case is that \p othervar is an image of the target variable
4512 * in a SCIP copy. Method should be applied with care, especially because no internal checks are performed whether
4513 * the history merge is reasonable
4514 *
4515 * @note Do not use this method if the two variables originate from two SCIP's with different objective functions, since
4516 * this corrupts the variable pseudo costs
4517 * @note Apply with care; no internal checks are performed if the two variables should be merged
4518 */
4519 void SCIPvarMergeHistories(
4520 SCIP_VAR* targetvar, /**< the variable that should contain both histories afterwards */
4521 SCIP_VAR* othervar, /**< the variable whose history is to be merged with that of the target variable */
4522 SCIP_STAT* stat /**< problem statistics */
4523 )
4524 {
4525 /* merge only the history of the current run into the target history */
4526 SCIPhistoryUnite(targetvar->history, othervar->historycrun, FALSE);
4527
4528 /* apply the changes also to the global history */
4529 SCIPhistoryUnite(stat->glbhistory, othervar->historycrun, FALSE);
4530 }
4531
4532 /** sets the history of a variable; this method is typically used within reoptimization to keep and update the variable
4533 * history over several iterations
4534 */
4535 void SCIPvarSetHistory(
4536 SCIP_VAR* var, /**< variable */
4537 SCIP_HISTORY* history, /**< the history which is to set */
4538 SCIP_STAT* stat /**< problem statistics */
4539 )
4540 {
4541 /* merge only the history of the current run into the target history */
4542 SCIPhistoryUnite(var->history, history, FALSE);
4543
4544 /* apply the changes also to the global history */
4545 SCIPhistoryUnite(stat->glbhistory, history, FALSE);
4546 }
4547
4548 /** tightens the bounds of both variables in aggregation x = a*y + c */
4549 static
4550 SCIP_RETCODE varUpdateAggregationBounds(
4551 SCIP_VAR* var, /**< problem variable */
4552 BMS_BLKMEM* blkmem, /**< block memory */
4553 SCIP_SET* set, /**< global SCIP settings */
4554 SCIP_STAT* stat, /**< problem statistics */
4555 SCIP_PROB* transprob, /**< tranformed problem data */
4556 SCIP_PROB* origprob, /**< original problem data */
4557 SCIP_PRIMAL* primal, /**< primal data */
4558 SCIP_TREE* tree, /**< branch and bound tree */
4559 SCIP_REOPT* reopt, /**< reoptimization data structure */
4560 SCIP_LP* lp, /**< current LP data */
4561 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
4562 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
4563 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
4564 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
4565 SCIP_VAR* aggvar, /**< variable y in aggregation x = a*y + c */
4566 SCIP_Real scalar, /**< multiplier a in aggregation x = a*y + c */
4567 SCIP_Real constant, /**< constant shift c in aggregation x = a*y + c */
4568 SCIP_Bool* infeasible, /**< pointer to store whether the aggregation is infeasible */
4569 SCIP_Bool* fixed /**< pointer to store whether the variables were fixed */
4570 )
4571 {
4572 SCIP_Real varlb;
4573 SCIP_Real varub;
4574 SCIP_Real aggvarlb;
4575 SCIP_Real aggvarub;
4576 SCIP_Bool aggvarbdschanged;
4577
4578 assert(var != NULL);
4579 assert(var->scip == set->scip);
4580 assert(aggvar != NULL);
4581 assert(!SCIPsetIsZero(set, scalar));
4582 assert(infeasible != NULL);
4583 assert(fixed != NULL);
4584
4585 *infeasible = FALSE;
4586 *fixed = FALSE;
4587
4588 SCIPsetDebugMsg(set, "updating bounds of variables in aggregation <%s> == %g*<%s> %+g\n", var->name, scalar, aggvar->name, constant);
4589 SCIPsetDebugMsg(set, " old bounds: <%s> [%g,%g] <%s> [%g,%g]\n",
4590 var->name, var->glbdom.lb, var->glbdom.ub, aggvar->name, aggvar->glbdom.lb, aggvar->glbdom.ub);
4591
4592 /* loop as long additional changes may be found */
4593 do
4594 {
4595 aggvarbdschanged = FALSE;
4596
4597 /* update the bounds of the aggregated variable x in x = a*y + c */
4598 if( scalar > 0.0 )
4599 {
4600 if( SCIPsetIsInfinity(set, -aggvar->glbdom.lb) )
4601 varlb = -SCIPsetInfinity(set);
4602 else
4603 varlb = aggvar->glbdom.lb * scalar + constant;
4604 if( SCIPsetIsInfinity(set, aggvar->glbdom.ub) )
4605 varub = SCIPsetInfinity(set);
4606 else
4607 varub = aggvar->glbdom.ub * scalar + constant;
4608 }
4609 else
4610 {
4611 if( SCIPsetIsInfinity(set, -aggvar->glbdom.lb) )
4612 varub = SCIPsetInfinity(set);
4613 else
4614 varub = aggvar->glbdom.lb * scalar + constant;
4615 if( SCIPsetIsInfinity(set, aggvar->glbdom.ub) )
4616 varlb = -SCIPsetInfinity(set);
4617 else
4618 varlb = aggvar->glbdom.ub * scalar + constant;
4619 }
4620 varlb = MAX(varlb, var->glbdom.lb);
4621 varub = MIN(varub, var->glbdom.ub);
4622 SCIPvarAdjustLb(var, set, &varlb);
4623 SCIPvarAdjustUb(var, set, &varub);
4624
4625 /* check the new bounds */
4626 if( SCIPsetIsGT(set, varlb, varub) )
4627 {
4628 /* the aggregation is infeasible */
4629 *infeasible = TRUE;
4630 return SCIP_OKAY;
4631 }
4632 else if( SCIPsetIsEQ(set, varlb, varub) )
4633 {
4634 /* the aggregated variable is fixed -> fix both variables */
4635 SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
4636 eventfilter, eventqueue, cliquetable, varlb, infeasible, fixed) );
4637 if( !(*infeasible) )
4638 {
4639 SCIP_Bool aggfixed;
4640
4641 SCIP_CALL( SCIPvarFix(aggvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
4642 eventfilter, eventqueue, cliquetable, (varlb-constant)/scalar, infeasible, &aggfixed) );
4643 assert(*fixed == aggfixed);
4644 }
4645 return SCIP_OKAY;
4646 }
4647 else
4648 {
4649 if( SCIPsetIsGT(set, varlb, var->glbdom.lb) )
4650 {
4651 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, varlb) );
4652 }
4653 if( SCIPsetIsLT(set, varub, var->glbdom.ub) )
4654 {
4655 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, varub) );
4656 }
4657
4658 /* update the hole list of the aggregation variable */
4659 /**@todo update hole list of aggregation variable */
4660 }
4661
4662 /* update the bounds of the aggregation variable y in x = a*y + c -> y = (x-c)/a */
4663 if( scalar > 0.0 )
4664 {
4665 if( SCIPsetIsInfinity(set, -var->glbdom.lb) )
4666 aggvarlb = -SCIPsetInfinity(set);
4667 else
4668 aggvarlb = (var->glbdom.lb - constant) / scalar;
4669 if( SCIPsetIsInfinity(set, var->glbdom.ub) )
4670 aggvarub = SCIPsetInfinity(set);
4671 else
4672 aggvarub = (var->glbdom.ub - constant) / scalar;
4673 }
4674 else
4675 {
4676 if( SCIPsetIsInfinity(set, -var->glbdom.lb) )
4677 aggvarub = SCIPsetInfinity(set);
4678 else
4679 aggvarub = (var->glbdom.lb - constant) / scalar;
4680 if( SCIPsetIsInfinity(set, var->glbdom.ub) )
4681 aggvarlb = -SCIPsetInfinity(set);
4682 else
4683 aggvarlb = (var->glbdom.ub - constant) / scalar;
4684 }
4685 aggvarlb = MAX(aggvarlb, aggvar->glbdom.lb);
4686 aggvarub = MIN(aggvarub, aggvar->glbdom.ub);
4687 SCIPvarAdjustLb(aggvar, set, &aggvarlb);
4688 SCIPvarAdjustUb(aggvar, set, &aggvarub);
4689
4690 /* check the new bounds */
4691 if( SCIPsetIsGT(set, aggvarlb, aggvarub) )
4692 {
4693 /* the aggregation is infeasible */
4694 *infeasible = TRUE;
4695 return SCIP_OKAY;
4696 }
4697 else if( SCIPsetIsEQ(set, aggvarlb, aggvarub) )
4698 {
4699 /* the aggregation variable is fixed -> fix both variables */
4700 SCIP_CALL( SCIPvarFix(aggvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
4701 eventfilter, eventqueue, cliquetable, aggvarlb, infeasible, fixed) );
4702 if( !(*infeasible) )
4703 {
4704 SCIP_Bool varfixed;
4705
4706 SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
4707 eventfilter, eventqueue, cliquetable, aggvarlb * scalar + constant, infeasible, &varfixed) );
4708 assert(*fixed == varfixed);
4709 }
4710 return SCIP_OKAY;
4711 }
4712 else
4713 {
4714 SCIP_Real oldbd;
4715 if( SCIPsetIsGT(set, aggvarlb, aggvar->glbdom.lb) )
4716 {
4717 oldbd = aggvar->glbdom.lb;
4718 SCIP_CALL( SCIPvarChgLbGlobal(aggvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, aggvarlb) );
4719 aggvarbdschanged = !SCIPsetIsEQ(set, oldbd, aggvar->glbdom.lb);
4720 }
4721 if( SCIPsetIsLT(set, aggvarub, aggvar->glbdom.ub) )
4722 {
4723 oldbd = aggvar->glbdom.ub;
4724 SCIP_CALL( SCIPvarChgUbGlobal(aggvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, aggvarub) );
4725 aggvarbdschanged = aggvarbdschanged || !SCIPsetIsEQ(set, oldbd, aggvar->glbdom.ub);
4726 }
4727
4728 /* update the hole list of the aggregation variable */
4729 /**@todo update hole list of aggregation variable */
4730 }
4731 }
4732 while( aggvarbdschanged );
4733
4734 SCIPsetDebugMsg(set, " new bounds: <%s> [%g,%g] <%s> [%g,%g]\n",
4735 var->name, var->glbdom.lb, var->glbdom.ub, aggvar->name, aggvar->glbdom.lb, aggvar->glbdom.ub);
4736
4737 return SCIP_OKAY;
4738 }
4739
4740 /** converts loose variable into aggregated variable */
4741 SCIP_RETCODE SCIPvarAggregate(
4742 SCIP_VAR* var, /**< loose problem variable */
4743 BMS_BLKMEM* blkmem, /**< block memory */
4744 SCIP_SET* set, /**< global SCIP settings */
4745 SCIP_STAT* stat, /**< problem statistics */
4746 SCIP_PROB* transprob, /**< tranformed problem data */
4747 SCIP_PROB* origprob, /**< original problem data */
4748 SCIP_PRIMAL* primal, /**< primal data */
4749 SCIP_TREE* tree, /**< branch and bound tree */
4750 SCIP_REOPT* reopt, /**< reoptimization data structure */
4751 SCIP_LP* lp, /**< current LP data */
4752 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
4753 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
4754 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
4755 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
4756 SCIP_VAR* aggvar, /**< loose variable y in aggregation x = a*y + c */
4757 SCIP_Real scalar, /**< multiplier a in aggregation x = a*y + c */
4758 SCIP_Real constant, /**< constant shift c in aggregation x = a*y + c */
4759 SCIP_Bool* infeasible, /**< pointer to store whether the aggregation is infeasible */
4760 SCIP_Bool* aggregated /**< pointer to store whether the aggregation was successful */
4761 )
4762 {
4763 SCIP_VAR** vars;
4764 SCIP_Real* coefs;
4765 SCIP_Real* constants;
4766 SCIP_Real obj;
4767 SCIP_Real branchfactor;
4768 SCIP_Bool fixed;
4769 int branchpriority;
4770 int nlocksdown[NLOCKTYPES];
4771 int nlocksup[NLOCKTYPES];
4772 int nvbds;
4773 int i;
4774 int j;
4775
4776 assert(var != NULL);
4777 assert(aggvar != NULL);
4778 assert(var->scip == set->scip);
4779 assert(var->glbdom.lb == var->locdom.lb); /*lint !e777*/
4780 assert(var->glbdom.ub == var->locdom.ub); /*lint !e777*/
4781 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
4782 assert(!SCIPeventqueueIsDelayed(eventqueue)); /* otherwise, the pseudo objective value update gets confused */
4783 assert(infeasible != NULL);
4784 assert(aggregated != NULL);
4785
4786 *infeasible = FALSE;
4787 *aggregated = FALSE;
4788
4789 /* get active problem variable of aggregation variable */
4790 SCIP_CALL( SCIPvarGetProbvarSum(&aggvar, set, &scalar, &constant) );
4791
4792 /* aggregation is a fixing, if the scalar is zero */
4793 if( SCIPsetIsZero(set, scalar) )
4794 {
4795 SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand, eventfilter,
4796 eventqueue, cliquetable, constant, infeasible, aggregated) );
4797 goto TERMINATE;
4798 }
4799
4800 /* don't perform the aggregation if the aggregation variable is multi-aggregated itself */
4801 if( SCIPvarGetStatus(aggvar) == SCIP_VARSTATUS_MULTAGGR )
4802 return SCIP_OKAY;
4803
4804 /**@todo currently we don't perform the aggregation if the aggregation variable has a non-empty hole list; this
4805 * should be changed in the future
4806 */
4807 if( SCIPvarGetHolelistGlobal(var) != NULL )
4808 return SCIP_OKAY;
4809
4810 /* if the variable is not allowed to be aggregated */
4811 if( SCIPvarDoNotAggr(var) )
4812 {
4813 SCIPsetDebugMsg(set, "variable is not allowed to be aggregated.\n");
4814 return SCIP_OKAY;
4815 }
4816
4817 assert(aggvar->glbdom.lb == aggvar->locdom.lb); /*lint !e777*/
4818 assert(aggvar->glbdom.ub == aggvar->locdom.ub); /*lint !e777*/
4819 assert(SCIPvarGetStatus(aggvar) == SCIP_VARSTATUS_LOOSE);
4820
4821 SCIPsetDebugMsg(set, "aggregate variable <%s>[%g,%g] == %g*<%s>[%g,%g] %+g\n", var->name, var->glbdom.lb, var->glbdom.ub,
4822 scalar, aggvar->name, aggvar->glbdom.lb, aggvar->glbdom.ub, constant);
4823
4824 /* if variable and aggregation variable are equal, the variable can be fixed: x == a*x + c => x == c/(1-a) */
4825 if( var == aggvar )
4826 {
4827 if( SCIPsetIsEQ(set, scalar, 1.0) )
4828 *infeasible = !SCIPsetIsZero(set, constant);
4829 else
4830 {
4831 SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
4832 eventfilter, eventqueue, cliquetable, constant/(1.0-scalar), infeasible, aggregated) );
4833 }
4834 goto TERMINATE;
4835 }
4836
4837 /* tighten the bounds of aggregated and aggregation variable */
4838 SCIP_CALL( varUpdateAggregationBounds(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
4839 branchcand, eventfilter, eventqueue, cliquetable, aggvar, scalar, constant, infeasible, &fixed) );
4840 if( *infeasible || fixed )
4841 {
4842 *aggregated = fixed;
4843 goto TERMINATE;
4844 }
4845
4846 /* delete implications and variable bounds of the aggregated variable from other variables, but keep them in the
4847 * aggregated variable
4848 */
4849 SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, FALSE) );
4850
4851 /* set the aggregated variable's objective value to 0.0 */
4852 obj = var->obj;
4853 SCIP_CALL( SCIPvarChgObj(var, blkmem, set, transprob, primal, lp, eventqueue, 0.0) );
4854
4855 /* unlock all locks */
4856 for( i = 0; i < NLOCKTYPES; i++ )
4857 {
4858 nlocksdown[i] = var->nlocksdown[i];
4859 nlocksup[i] = var->nlocksup[i];
4860
4861 var->nlocksdown[i] = 0;
4862 var->nlocksup[i] = 0;
4863 }
4864
4865 /* check, if variable should be used as NEGATED variable of the aggregation variable */
4866 if( SCIPvarIsBinary(var) && SCIPvarIsBinary(aggvar)
4867 && var->negatedvar == NULL && aggvar->negatedvar == NULL
4868 && SCIPsetIsEQ(set, scalar, -1.0) && SCIPsetIsEQ(set, constant, 1.0) )
4869 {
4870 /* link both variables as negation pair */
4871 var->varstatus = SCIP_VARSTATUS_NEGATED; /*lint !e641*/
4872 var->data.negate.constant = 1.0;
4873 var->negatedvar = aggvar;
4874 aggvar->negatedvar = var;
4875
4876 /* copy donot(mult)aggr status */
4877 aggvar->donotaggr |= var->donotaggr;
4878 aggvar->donotmultaggr |= var->donotmultaggr;
4879
4880 /* mark both variables to be non-deletable */
4881 SCIPvarMarkNotDeletable(var);
4882 SCIPvarMarkNotDeletable(aggvar);
4883 }
4884 else
4885 {
4886 /* convert variable into aggregated variable */
4887 var->varstatus = SCIP_VARSTATUS_AGGREGATED; /*lint !e641*/
4888 var->data.aggregate.var = aggvar;
4889 var->data.aggregate.scalar = scalar;
4890 var->data.aggregate.constant = constant;
4891
4892 /* copy donot(mult)aggr status */
4893 aggvar->donotaggr |= var->donotaggr;
4894 aggvar->donotmultaggr |= var->donotmultaggr;
4895
4896 /* mark both variables to be non-deletable */
4897 SCIPvarMarkNotDeletable(var);
4898 SCIPvarMarkNotDeletable(aggvar);
4899 }
4900
4901 /* make aggregated variable a parent of the aggregation variable */
4902 SCIP_CALL( varAddParent(aggvar, blkmem, set, var) );
4903
4904 /* relock the variable, thus increasing the locks of the aggregation variable */
4905 for( i = 0; i < NLOCKTYPES; i++ )
4906 {
4907 SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, nlocksdown[i], nlocksup[i]) );
4908 }
4909
4910 /* move the variable bounds to the aggregation variable:
4911 * - add all variable bounds again to the variable, thus adding it to the aggregation variable
4912 * - free the variable bounds data structures
4913 */
4914 if( var->vlbs != NULL )
4915 {
4916 nvbds = SCIPvboundsGetNVbds(var->vlbs);
4917 vars = SCIPvboundsGetVars(var->vlbs);
4918 coefs = SCIPvboundsGetCoefs(var->vlbs);
4919 constants = SCIPvboundsGetConstants(var->vlbs);
4920 for( i = 0; i < nvbds && !(*infeasible); ++i )
4921 {
4922 SCIP_CALL( SCIPvarAddVlb(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable, branchcand,
4923 eventqueue, vars[i], coefs[i], constants[i], FALSE, infeasible, NULL) );
4924 }
4925 }
4926 if( var->vubs != NULL )
4927 {
4928 nvbds = SCIPvboundsGetNVbds(var->vubs);
4929 vars = SCIPvboundsGetVars(var->vubs);
4930 coefs = SCIPvboundsGetCoefs(var->vubs);
4931 constants = SCIPvboundsGetConstants(var->vubs);
4932 for( i = 0; i < nvbds && !(*infeasible); ++i )
4933 {
4934 SCIP_CALL( SCIPvarAddVub(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable, branchcand,
4935 eventqueue, vars[i], coefs[i], constants[i], FALSE, infeasible, NULL) );
4936 }
4937 }
4938 SCIPvboundsFree(&var->vlbs, blkmem);
4939 SCIPvboundsFree(&var->vubs, blkmem);
4940
4941 /* move the implications to the aggregation variable:
4942 * - add all implications again to the variable, thus adding it to the aggregation variable
4943 * - free the implications data structures
4944 */
4945 if( var->implics != NULL && SCIPvarGetType(aggvar) == SCIP_VARTYPE_BINARY )
4946 {
4947 assert(SCIPvarIsBinary(var));
4948 for( i = 0; i < 2; ++i )
4949 {
4950 SCIP_VAR** implvars;
4951 SCIP_BOUNDTYPE* impltypes;
4952 SCIP_Real* implbounds;
4953 int nimpls;
4954
4955 nimpls = SCIPimplicsGetNImpls(var->implics, (SCIP_Bool)i);
4956 implvars = SCIPimplicsGetVars(var->implics, (SCIP_Bool)i);
4957 impltypes = SCIPimplicsGetTypes(var->implics, (SCIP_Bool)i);
4958 implbounds = SCIPimplicsGetBounds(var->implics, (SCIP_Bool)i);
4959
4960 for( j = 0; j < nimpls && !(*infeasible); ++j )
4961 {
4962 /* @todo can't we omit transitive closure, because it should already have been done when adding the
4963 * implication to the aggregated variable?
4964 */
4965 SCIP_CALL( SCIPvarAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
4966 branchcand, eventqueue, (SCIP_Bool)i, implvars[j], impltypes[j], implbounds[j], FALSE, infeasible,
4967 NULL) );
4968 assert(nimpls == SCIPimplicsGetNImpls(var->implics, (SCIP_Bool)i));
4969 }
4970 }
4971 }
4972 SCIPimplicsFree(&var->implics, blkmem);
4973
4974 /* add the history entries to the aggregation variable and clear the history of the aggregated variable */
4975 SCIPhistoryUnite(aggvar->history, var->history, scalar < 0.0);
4976 SCIPhistoryUnite(aggvar->historycrun, var->historycrun, scalar < 0.0);
4977 SCIPhistoryReset(var->history);
4978 SCIPhistoryReset(var->historycrun);
4979
4980 /* update flags of aggregation variable */
4981 aggvar->removable &= var->removable;
4982
4983 /* update branching factors and priorities of both variables to be the maximum of both variables */
4984 branchfactor = MAX(aggvar->branchfactor, var->branchfactor);
4985 branchpriority = MAX(aggvar->branchpriority, var->branchpriority);
4986 SCIP_CALL( SCIPvarChgBranchFactor(aggvar, set, branchfactor) );
4987 SCIP_CALL( SCIPvarChgBranchPriority(aggvar, branchpriority) );
4988 SCIP_CALL( SCIPvarChgBranchFactor(var, set, branchfactor) );
4989 SCIP_CALL( SCIPvarChgBranchPriority(var, branchpriority) );
4990
4991 /* update branching direction of both variables to agree to a single direction */
4992 if( scalar >= 0.0 )
4993 {
4994 if( (SCIP_BRANCHDIR)var->branchdirection == SCIP_BRANCHDIR_AUTO )
4995 {
4996 SCIP_CALL( SCIPvarChgBranchDirection(var, (SCIP_BRANCHDIR)aggvar->branchdirection) );
4997 }
4998 else if( (SCIP_BRANCHDIR)aggvar->branchdirection == SCIP_BRANCHDIR_AUTO )
4999 {
5000 SCIP_CALL( SCIPvarChgBranchDirection(aggvar, (SCIP_BRANCHDIR)var->branchdirection) );
5001 }
5002 else if( var->branchdirection != aggvar->branchdirection )
5003 {
5004 SCIP_CALL( SCIPvarChgBranchDirection(var, SCIP_BRANCHDIR_AUTO) );
5005 }
5006 }
5007 else
5008 {
5009 if( (SCIP_BRANCHDIR)var->branchdirection == SCIP_BRANCHDIR_AUTO )
5010 {
5011 SCIP_CALL( SCIPvarChgBranchDirection(var, SCIPbranchdirOpposite((SCIP_BRANCHDIR)aggvar->branchdirection)) );
5012 }
5013 else if( (SCIP_BRANCHDIR)aggvar->branchdirection == SCIP_BRANCHDIR_AUTO )
5014 {
5015 SCIP_CALL( SCIPvarChgBranchDirection(aggvar, SCIPbranchdirOpposite((SCIP_BRANCHDIR)var->branchdirection)) );
5016 }
5017 else if( var->branchdirection != aggvar->branchdirection )
5018 {
5019 SCIP_CALL( SCIPvarChgBranchDirection(var, SCIP_BRANCHDIR_AUTO) );
5020 }
5021 }
5022
5023 if( var->probindex != -1 )
5024 {
5025 /* inform problem about the variable's status change */
5026 SCIP_CALL( SCIPprobVarChangedStatus(transprob, blkmem, set, branchcand, cliquetable, var) );
5027 }
5028
5029 /* reset the objective value of the aggregated variable, thus adjusting the objective value of the aggregation
5030 * variable and the problem's objective offset
5031 */
5032 SCIP_CALL( SCIPvarAddObj(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, eventfilter, eventqueue, obj) );
5033
5034 /* issue VARFIXED event */
5035 SCIP_CALL( varEventVarFixed(var, blkmem, set, eventqueue, 1) );
5036
5037 *aggregated = TRUE;
5038
5039 TERMINATE:
5040 /* check aggregation on debugging solution */
5041 if( *infeasible || *aggregated )
5042 SCIP_CALL( SCIPdebugCheckAggregation(set, var, &aggvar, &scalar, constant, 1) ); /*lint !e506 !e774*/
5043
5044 return SCIP_OKAY;
5045 }
5046
5047 /** Tries to aggregate an equality a*x + b*y == c consisting of two (implicit) integral active problem variables x and
5048 * y. An integer aggregation (i.e. integral coefficients a' and b', such that a'*x + b'*y == c') is searched.
5049 *
5050 * This can lead to the detection of infeasibility (e.g. if c' is fractional), or to a rejection of the aggregation
5051 * (denoted by aggregated == FALSE), if the resulting integer coefficients are too large and thus numerically instable.
5052 */
5053 static
5054 SCIP_RETCODE tryAggregateIntVars(
5055 SCIP_SET* set, /**< global SCIP settings */
5056 BMS_BLKMEM* blkmem, /**< block memory */
5057 SCIP_STAT* stat, /**< problem statistics */
5058 SCIP_PROB* transprob, /**< tranformed problem data */
5059 SCIP_PROB* origprob, /**< original problem data */
5060 SCIP_PRIMAL* primal, /**< primal data */
5061 SCIP_TREE* tree, /**< branch and bound tree */
5062 SCIP_REOPT* reopt, /**< reoptimization data structure */
5063 SCIP_LP* lp, /**< current LP data */
5064 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
5065 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
5066 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
5067 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
5068 SCIP_VAR* varx, /**< integral variable x in equality a*x + b*y == c */
5069 SCIP_VAR* vary, /**< integral variable y in equality a*x + b*y == c */
5070 SCIP_Real scalarx, /**< multiplier a in equality a*x + b*y == c */
5071 SCIP_Real scalary, /**< multiplier b in equality a*x + b*y == c */
5072 SCIP_Real rhs, /**< right hand side c in equality a*x + b*y == c */
5073 SCIP_Bool* infeasible, /**< pointer to store whether the aggregation is infeasible */
5074 SCIP_Bool* aggregated /**< pointer to store whether the aggregation was successful */
5075 )
5076 {
5077 SCIP_VAR* aggvar;
5078 char aggvarname[SCIP_MAXSTRLEN];
5079 SCIP_Longint scalarxn = 0;
5080 SCIP_Longint scalarxd = 0;
5081 SCIP_Longint scalaryn = 0;
5082 SCIP_Longint scalaryd = 0;
5083 SCIP_Longint a;
5084 SCIP_Longint b;
5085 SCIP_Longint c;
5086 SCIP_Longint scm;
5087 SCIP_Longint gcd;
5088 SCIP_Longint currentclass;
5089 SCIP_Longint classstep;
5090 SCIP_Longint xsol;
5091 SCIP_Longint ysol;
5092 SCIP_Bool success;
5093 SCIP_VARTYPE vartype;
5094
5095 #define MAXDNOM 1000000LL
5096
5097 assert(set != NULL);
5098 assert(blkmem != NULL);
5099 assert(stat != NULL);
5100 assert(transprob != NULL);
5101 assert(origprob != NULL);
5102 assert(tree != NULL);
5103 assert(lp != NULL);
5104 assert(cliquetable != NULL);
5105 assert(branchcand != NULL);
5106 assert(eventqueue != NULL);
5107 assert(varx != NULL);
5108 assert(vary != NULL);
5109 assert(varx != vary);
5110 assert(infeasible != NULL);
5111 assert(aggregated != NULL);
5112 assert(SCIPsetGetStage(set) == SCIP_STAGE_PRESOLVING);
5113 assert(SCIPvarGetStatus(varx) == SCIP_VARSTATUS_LOOSE);
5114 assert(SCIPvarGetType(varx) == SCIP_VARTYPE_INTEGER || SCIPvarGetType(varx) == SCIP_VARTYPE_IMPLINT);
5115 assert(SCIPvarGetStatus(vary) == SCIP_VARSTATUS_LOOSE);
5116 assert(SCIPvarGetType(vary) == SCIP_VARTYPE_INTEGER || SCIPvarGetType(vary) == SCIP_VARTYPE_IMPLINT);
5117 assert(!SCIPsetIsZero(set, scalarx));
5118 assert(!SCIPsetIsZero(set, scalary));
5119
5120 *infeasible = FALSE;
5121 *aggregated = FALSE;
5122
5123 /* if the variable is not allowed to be aggregated */
5124 if( SCIPvarDoNotAggr(varx) )
5125 {
5126 SCIPsetDebugMsg(set, "variable is not allowed to be aggregated.\n");
5127 return SCIP_OKAY;
5128 }
5129
5130 /* get rational representation of coefficients */
5131 success = SCIPrealToRational(scalarx, -SCIPsetEpsilon(set), SCIPsetEpsilon(set), MAXDNOM, &scalarxn, &scalarxd);
5132 if( success )
5133 success = SCIPrealToRational(scalary, -SCIPsetEpsilon(set), SCIPsetEpsilon(set), MAXDNOM, &scalaryn, &scalaryd);
5134 if( !success )
5135 return SCIP_OKAY;
5136 assert(scalarxd >= 1);
5137 assert(scalaryd >= 1);
5138
5139 /* multiply equality with smallest common denominator */
5140 scm = SCIPcalcSmaComMul(scalarxd, scalaryd);
5141 a = (scm/scalarxd)*scalarxn;
5142 b = (scm/scalaryd)*scalaryn;
5143 rhs *= scm;
5144
5145 /* divide equality by the greatest common divisor of a and b */
5146 gcd = SCIPcalcGreComDiv(ABS(a), ABS(b));
5147 a /= gcd;
5148 b /= gcd;
5149 rhs /= gcd;
5150 assert(a != 0);
5151 assert(b != 0);
5152
5153 /* check, if right hand side is integral */
5154 if( !SCIPsetIsFeasIntegral(set, rhs) )
5155 {
5156 *infeasible = TRUE;
5157 return SCIP_OKAY;
5158 }
5159 c = (SCIP_Longint)(SCIPsetFeasFloor(set, rhs));
5160
5161 /* check that the scalar and constant in the aggregation are not too large to avoid numerical problems */
5162 if( REALABS((SCIP_Real)(c/a)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) /*lint !e653*/
5163 || REALABS((SCIP_Real)(b)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) /*lint !e653*/
5164 || REALABS((SCIP_Real)(a)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) ) /*lint !e653*/
5165 {
5166 return SCIP_OKAY;
5167 }
5168
5169 /* check, if we are in an easy case with either |a| = 1 or |b| = 1 */
5170 if( (a == 1 || a == -1) && SCIPvarGetType(vary) == SCIP_VARTYPE_INTEGER )
5171 {
5172 /* aggregate x = - b/a*y + c/a */
5173 /*lint --e{653}*/
5174 SCIP_CALL( SCIPvarAggregate(varx, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
5175 branchcand, eventfilter, eventqueue, vary, (SCIP_Real)(-b/a), (SCIP_Real)(c/a), infeasible, aggregated) );
5176 assert(*aggregated);
5177 return SCIP_OKAY;
5178 }
5179 if( (b == 1 || b == -1) && SCIPvarGetType(varx) == SCIP_VARTYPE_INTEGER )
5180 {
5181 /* aggregate y = - a/b*x + c/b */
5182 /*lint --e{653}*/
5183 SCIP_CALL( SCIPvarAggregate(vary, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
5184 branchcand, eventfilter, eventqueue, varx, (SCIP_Real)(-a/b), (SCIP_Real)(c/b), infeasible, aggregated) );
5185 assert(*aggregated);
5186 return SCIP_OKAY;
5187 }
5188
5189 /* Both variables are integers, their coefficients are not multiples of each other, and they don't have any
5190 * common divisor. Let (x',y') be a solution of the equality
5191 * a*x + b*y == c -> a*x == c - b*y
5192 * Then x = -b*z + x', y = a*z + y' with z integral gives all solutions to the equality.
5193 */
5194
5195 /* find initial solution (x',y'):
5196 * - find y' such that c - b*y' is a multiple of a
5197 * - start in equivalence class c%a
5198 * - step through classes, where each step increases class number by (-b)%a, until class 0 is visited
5199 * - if equivalence class 0 is visited, we are done: y' equals the number of steps taken
5200 * - 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
5201 * - calculate x' with x' = (c - b*y')/a (which must be integral)
5202 *
5203 * Algorithm works for a > 0 only.
5204 */
5205 if( a < 0 )
5206 {
5207 a = -a;
5208 b = -b;
5209 c = -c;
5210 }
5211 assert(a > 0);
5212
5213 /* search upwards from ysol = 0 */
5214 ysol = 0;
5215 currentclass = c % a;
5216 if( currentclass < 0 )
5217 currentclass += a;
5218 assert(0 <= currentclass && currentclass < a);
5219
5220 classstep = (-b) % a;
5221
5222 if( classstep < 0 )
5223 classstep += a;
5224 assert(0 <= classstep && classstep < a);
5225
5226 while( currentclass != 0 )
5227 {
5228 assert(0 <= currentclass && currentclass < a);
5229 currentclass += classstep;
5230 if( currentclass >= a )
5231 currentclass -= a;
5232 ysol++;
5233 }
5234 assert(ysol < a);
5235 assert(((c - b*ysol) % a) == 0);
5236
5237 xsol = (c - b*ysol)/a;
5238
5239 /* determine variable type for new artificial variable:
5240 *
5241 * if both variables are implicit integer the new variable can be implicit too, because the integer implication on
5242 * these both variables should be enforced by some other variables, otherwise the new variable needs to be of
5243 * integral type
5244 */
5245 vartype = ((SCIPvarGetType(varx) == SCIP_VARTYPE_INTEGER || SCIPvarGetType(vary) == SCIP_VARTYPE_INTEGER)
5246 ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_IMPLINT);
5247
5248 /* feasible solutions are (x,y) = (x',y') + z*(-b,a)
5249 * - create new integer variable z with infinite bounds
5250 * - aggregate variable x = -b*z + x'
5251 * - aggregate variable y = a*z + y'
5252 * - the bounds of z are calculated automatically during aggregation
5253 */
5254 (void) SCIPsnprintf(aggvarname, SCIP_MAXSTRLEN, "agg%d", stat->nvaridx);
5255 SCIP_CALL( SCIPvarCreateTransformed(&aggvar, blkmem, set, stat,
5256 aggvarname, -SCIPsetInfinity(set), SCIPsetInfinity(set), 0.0, vartype,
5257 SCIPvarIsInitial(varx) || SCIPvarIsInitial(vary), SCIPvarIsRemovable(varx) && SCIPvarIsRemovable(vary),
5258 NULL, NULL, NULL, NULL, NULL) );
5259
5260 SCIP_CALL( SCIPprobAddVar(transprob, blkmem, set, lp, branchcand, eventfilter, eventqueue, aggvar) );
5261
5262 SCIP_CALL( SCIPvarAggregate(varx, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
5263 branchcand, eventfilter, eventqueue, aggvar, (SCIP_Real)(-b), (SCIP_Real)xsol, infeasible, aggregated) );
5264 assert(*aggregated || *infeasible);
5265
5266 if( !(*infeasible) )
5267 {
5268 SCIP_CALL( SCIPvarAggregate(vary, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
5269 branchcand, eventfilter, eventqueue, aggvar, (SCIP_Real)a, (SCIP_Real)ysol, infeasible, aggregated) );
5270 assert(*aggregated || *infeasible);
5271 }
5272
5273 /* release z */
5274 SCIP_CALL( SCIPvarRelease(&aggvar, blkmem, set, eventqueue, lp) );
5275
5276 return SCIP_OKAY; /*lint !e438*/
5277 }
5278
5279 /** performs second step of SCIPaggregateVars():
5280 * the variable to be aggregated is chosen among active problem variables x' and y', preferring a less strict variable
5281 * type as aggregation variable (i.e. continuous variables are preferred over implicit integers, implicit integers
5282 * or integers over binaries). If none of the variables is continuous, it is tried to find an integer
5283 * aggregation (i.e. integral coefficients a'' and b'', such that a''*x' + b''*y' == c''). This can lead to
5284 * the detection of infeasibility (e.g. if c'' is fractional), or to a rejection of the aggregation (denoted by
5285 * aggregated == FALSE), if the resulting integer coefficients are too large and thus numerically instable.
5286 *
5287 * @todo check for fixings, infeasibility, bound changes, or domain holes:
5288 * a) if there is no easy aggregation and we have one binary variable and another integer/implicit/binary variable
5289 * b) for implicit integer variables with fractional aggregation scalar (we cannot (for technical reasons) and do
5290 * not want to aggregate implicit integer variables, since we loose the corresponding divisibility property)
5291 */
5292 SCIP_RETCODE SCIPvarTryAggregateVars(
5293 SCIP_SET* set, /**< global SCIP settings */
5294 BMS_BLKMEM* blkmem, /**< block memory */
5295 SCIP_STAT* stat, /**< problem statistics */
5296 SCIP_PROB* transprob, /**< tranformed problem data */
5297 SCIP_PROB* origprob, /**< original problem data */
5298 SCIP_PRIMAL* primal, /**< primal data */
5299 SCIP_TREE* tree, /**< branch and bound tree */
5300 SCIP_REOPT* reopt, /**< reoptimization data structure */
5301 SCIP_LP* lp, /**< current LP data */
5302 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
5303 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
5304 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
5305 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
5306 SCIP_VAR* varx, /**< variable x in equality a*x + b*y == c */
5307 SCIP_VAR* vary, /**< variable y in equality a*x + b*y == c */
5308 SCIP_Real scalarx, /**< multiplier a in equality a*x + b*y == c */
5309 SCIP_Real scalary, /**< multiplier b in equality a*x + b*y == c */
5310 SCIP_Real rhs, /**< right hand side c in equality a*x + b*y == c */
5311 SCIP_Bool* infeasible, /**< pointer to store whether the aggregation is infeasible */
5312 SCIP_Bool* aggregated /**< pointer to store whether the aggregation was successful */
5313 )
5314 {
5315 SCIP_Bool easyaggr;
5316
5317 assert(set != NULL);
5318 assert(blkmem != NULL);
5319 assert(stat != NULL);
5320 assert(transprob != NULL);
5321 assert(origprob != NULL);
5322 assert(tree != NULL);
5323 assert(lp != NULL);
5324 assert(cliquetable != NULL);
5325 assert(branchcand != NULL);
5326 assert(eventqueue != NULL);
5327 assert(varx != NULL);
5328 assert(vary != NULL);
5329 assert(varx != vary);
5330 assert(infeasible != NULL);
5331 assert(aggregated != NULL);
5332 assert(SCIPsetGetStage(set) == SCIP_STAGE_PRESOLVING);
5333 assert(SCIPvarGetStatus(varx) == SCIP_VARSTATUS_LOOSE);
5334 assert(SCIPvarGetStatus(vary) == SCIP_VARSTATUS_LOOSE);
5335 assert(!SCIPsetIsZero(set, scalarx));
5336 assert(!SCIPsetIsZero(set, scalary));
5337
5338 *infeasible = FALSE;
5339 *aggregated = FALSE;
5340
5341 if( SCIPsetIsZero(set, scalarx / scalary) || SCIPsetIsZero(set, scalary / scalarx) )
5342 return SCIP_OKAY;
5343
5344 /* prefer aggregating the variable of more general type (preferred aggregation variable is varx) */
5345 if( SCIPvarGetType(vary) > SCIPvarGetType(varx) ||
5346 (SCIPvarGetType(vary) == SCIPvarGetType(varx) && !SCIPvarIsBinary(vary) && SCIPvarIsBinary(varx)) )
5347 {
5348 SCIP_VAR* var;
5349 SCIP_Real scalar;
5350
5351 /* switch the variables, such that varx is the variable of more general type (cont > implint > int > bin) */
5352 var = vary;
5353 vary = varx;
5354 varx = var;
5355 scalar = scalary;
5356 scalary = scalarx;
5357 scalarx = scalar;
5358 }
5359
5360 /* don't aggregate if the aggregation would lead to a binary variable aggregated to a non-binary variable */
5361 if( SCIPvarIsBinary(varx) && !SCIPvarIsBinary(vary) )
5362 return SCIP_OKAY;
5363
5364 assert(SCIPvarGetType(varx) >= SCIPvarGetType(vary));
5365
5366 /* figure out, which variable should be aggregated */
5367 easyaggr = FALSE;
5368
5369 /* check if it is an easy aggregation */
5370 if( SCIPvarGetType(varx) == SCIP_VARTYPE_CONTINUOUS && SCIPvarGetType(vary) < SCIP_VARTYPE_CONTINUOUS )
5371 {
5372 easyaggr = TRUE;
5373 }
5374 else if( SCIPsetIsFeasIntegral(set, scalary/scalarx) )
5375 {
5376 easyaggr = TRUE;
5377 }
5378 else if( SCIPsetIsFeasIntegral(set, scalarx/scalary) && SCIPvarGetType(vary) == SCIPvarGetType(varx) )
5379 {
5380 /* we have an easy aggregation if we flip the variables x and y */
5381 SCIP_VAR* var;
5382 SCIP_Real scalar;
5383
5384 /* switch the variables, such that varx is the aggregated variable */
5385 var = vary;
5386 vary = varx;
5387 varx = var;
5388 scalar = scalary;
5389 scalary = scalarx;
5390 scalarx = scalar;
5391 easyaggr = TRUE;
5392 }
5393 else if( SCIPvarGetType(varx) == SCIP_VARTYPE_CONTINUOUS )
5394 {
5395 /* the aggregation is still easy if both variables are continuous */
5396 assert(SCIPvarGetType(vary) == SCIP_VARTYPE_CONTINUOUS); /* otherwise we are in the first case */
5397 easyaggr = TRUE;
5398 }
5399
5400 /* did we find an "easy" aggregation? */
5401 if( easyaggr )
5402 {
5403 SCIP_Real scalar;
5404 SCIP_Real constant;
5405
5406 assert(SCIPvarGetType(varx) >= SCIPvarGetType(vary));
5407
5408 /* calculate aggregation scalar and constant: a*x + b*y == c => x == -b/a * y + c/a */
5409 scalar = -scalary/scalarx;
5410 constant = rhs/scalarx;
5411
5412 if( REALABS(constant) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) ) /*lint !e653*/
5413 return SCIP_OKAY;
5414
5415 /* check aggregation for integer feasibility */
5416 if( SCIPvarGetType(varx) != SCIP_VARTYPE_CONTINUOUS
5417 && SCIPvarGetType(vary) != SCIP_VARTYPE_CONTINUOUS
5418 && SCIPsetIsFeasIntegral(set, scalar) && !SCIPsetIsFeasIntegral(set, constant) )
5419 {
5420 *infeasible = TRUE;
5421 return SCIP_OKAY;
5422 }
5423
5424 /* if the aggregation scalar is fractional, we cannot (for technical reasons) and do not want to aggregate implicit integer variables,
5425 * since then we would loose the corresponding divisibility property
5426 */
5427 assert(SCIPvarGetType(varx) != SCIP_VARTYPE_IMPLINT || SCIPsetIsFeasIntegral(set, scalar));
5428
5429 /* aggregate the variable */
5430 SCIP_CALL( SCIPvarAggregate(varx, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
5431 branchcand, eventfilter, eventqueue, vary, scalar, constant, infeasible, aggregated) );
5432 assert(*aggregated || *infeasible || SCIPvarDoNotAggr(varx));
5433 }
5434 else if( (SCIPvarGetType(varx) == SCIP_VARTYPE_INTEGER || SCIPvarGetType(varx) == SCIP_VARTYPE_IMPLINT)
5435 && (SCIPvarGetType(vary) == SCIP_VARTYPE_INTEGER || SCIPvarGetType(vary) == SCIP_VARTYPE_IMPLINT) )
5436 {
5437 /* the variables are both integral: we have to try to find an integer aggregation */
5438 SCIP_CALL( tryAggregateIntVars(set, blkmem, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
5439 branchcand, eventfilter, eventqueue, varx, vary, scalarx, scalary, rhs, infeasible, aggregated) );
5440 }
5441
5442 return SCIP_OKAY;
5443 }
5444
5445 /** converts variable into multi-aggregated variable */
5446 SCIP_RETCODE SCIPvarMultiaggregate(
5447 SCIP_VAR* var, /**< problem variable */
5448 BMS_BLKMEM* blkmem, /**< block memory */
5449 SCIP_SET* set, /**< global SCIP settings */
5450 SCIP_STAT* stat, /**< problem statistics */
5451 SCIP_PROB* transprob, /**< tranformed problem data */
5452 SCIP_PROB* origprob, /**< original problem data */
5453 SCIP_PRIMAL* primal, /**< primal data */
5454 SCIP_TREE* tree, /**< branch and bound tree */
5455 SCIP_REOPT* reopt, /**< reoptimization data structure */
5456 SCIP_LP* lp, /**< current LP data */
5457 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
5458 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
5459 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
5460 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
5461 int naggvars, /**< number n of variables in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
5462 SCIP_VAR** aggvars, /**< variables y_i in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
5463 SCIP_Real* scalars, /**< multipliers a_i in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
5464 SCIP_Real constant, /**< constant shift c in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
5465 SCIP_Bool* infeasible, /**< pointer to store whether the aggregation is infeasible */
5466 SCIP_Bool* aggregated /**< pointer to store whether the aggregation was successful */
5467 )
5468 {
5469 SCIP_VAR** tmpvars;
5470 SCIP_Real* tmpscalars;
5471 SCIP_Real obj;
5472 SCIP_Real branchfactor;
5473 int branchpriority;
5474 SCIP_BRANCHDIR branchdirection;
5475 int nlocksdown[NLOCKTYPES];
5476 int nlocksup[NLOCKTYPES];
5477 int v;
5478 SCIP_Real tmpconstant;
5479 SCIP_Real tmpscalar;
5480 int ntmpvars;
5481 int tmpvarssize;
5482 int tmprequiredsize;
5483 int i;
5484
5485 assert(var != NULL);
5486 assert(var->scip == set->scip);
5487 assert(var->glbdom.lb == var->locdom.lb); /*lint !e777*/
5488 assert(var->glbdom.ub == var->locdom.ub); /*lint !e777*/
5489 assert(naggvars == 0 || aggvars != NULL);
5490 assert(naggvars == 0 || scalars != NULL);
5491 assert(infeasible != NULL);
5492 assert(aggregated != NULL);
5493
5494 SCIPsetDebugMsg(set, "trying multi-aggregating variable <%s> == ...%d vars... %+g\n", var->name, naggvars, constant);
5495
5496 *infeasible = FALSE;
5497 *aggregated = FALSE;
5498
5499 switch( SCIPvarGetStatus(var) )
5500 {
5501 case SCIP_VARSTATUS_ORIGINAL:
5502 if( var->data.original.transvar == NULL )
5503 {
5504 SCIPerrorMessage("cannot multi-aggregate an untransformed original variable\n");
5505 return SCIP_INVALIDDATA;
5506 }
5507 SCIP_CALL( SCIPvarMultiaggregate(var->data.original.transvar, blkmem, set, stat, transprob, origprob, primal, tree,
5508 reopt, lp, cliquetable, branchcand, eventfilter, eventqueue, naggvars, aggvars, scalars, constant, infeasible, aggregated) );
5509 break;
5510
5511 case SCIP_VARSTATUS_LOOSE:
5512 assert(!SCIPeventqueueIsDelayed(eventqueue)); /* otherwise, the pseudo objective value update gets confused */
5513
5514 /* check if we would create a self-reference */
5515 ntmpvars = naggvars;
5516 tmpvarssize = naggvars;
5517 tmpconstant = constant;
5518 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &tmpvars, aggvars, ntmpvars) );
5519 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &tmpscalars, scalars, ntmpvars) );
5520
5521 /* get all active variables for multi-aggregation */
5522 SCIP_CALL( SCIPvarGetActiveRepresentatives(set, tmpvars, tmpscalars, &ntmpvars, tmpvarssize, &tmpconstant, &tmprequiredsize, FALSE) );
5523 if( tmprequiredsize > tmpvarssize )
5524 {
5525 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &tmpvars, tmpvarssize, tmprequiredsize) );
5526 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &tmpscalars, tmpvarssize, tmprequiredsize) );
5527 tmpvarssize = tmprequiredsize;
5528 SCIP_CALL( SCIPvarGetActiveRepresentatives(set, tmpvars, tmpscalars, &ntmpvars, tmpvarssize, &tmpconstant, &tmprequiredsize, FALSE) );
5529 assert( tmprequiredsize <= tmpvarssize );
5530 }
5531
5532 tmpscalar = 0.0;
5533
5534 /* iterate over all active variables of the multi-aggregation and filter all variables which are equal to the
5535 * possible multi-aggregated variable
5536 */
5537 for( v = ntmpvars - 1; v >= 0; --v )
5538 {
5539 assert(tmpvars[v] != NULL);
5540 assert(SCIPvarGetStatus(tmpvars[v]) == SCIP_VARSTATUS_LOOSE);
5541
5542 if( tmpvars[v]->index == var->index )
5543 {
5544 tmpscalar += tmpscalars[v];
5545 tmpvars[v] = tmpvars[ntmpvars - 1];
5546 tmpscalars[v] = tmpscalars[ntmpvars - 1];
5547 --ntmpvars;
5548 }
5549 }
5550
5551 /* this means that x = x + a_1*y_1 + ... + a_n*y_n + c */
5552 if( SCIPsetIsEQ(set, tmpscalar, 1.0) )
5553 {
5554 if( ntmpvars == 0 )
5555 {
5556 if( SCIPsetIsZero(set, tmpconstant) ) /* x = x */
5557 {
5558 SCIPsetDebugMsg(set, "Possible multi-aggregation was completely resolved and detected to be redundant.\n");
5559 goto TERMINATE;
5560 }
5561 else /* 0 = c and c != 0 */
5562 {
5563 SCIPsetDebugMsg(set, "Multi-aggregation was completely resolved and led to infeasibility.\n");
5564 *infeasible = TRUE;
5565 goto TERMINATE;
5566 }
5567 }
5568 else if( ntmpvars == 1 ) /* 0 = a*y + c => y = -c/a */
5569 {
5570 assert(tmpscalars[0] != 0.0);
5571 assert(tmpvars[0] != NULL);
5572
5573 SCIPsetDebugMsg(set, "Possible multi-aggregation led to fixing of variable <%s> to %g.\n", SCIPvarGetName(tmpvars[0]), -constant/tmpscalars[0]);
5574 SCIP_CALL( SCIPvarFix(tmpvars[0], blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
5575 branchcand, eventfilter, eventqueue, cliquetable, -constant/tmpscalars[0], infeasible, aggregated) );
5576 goto TERMINATE;
5577 }
5578 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 */
5579 {
5580 /* both variables are different active problem variables, and both scalars are non-zero: try to aggregate them */
5581 SCIPsetDebugMsg(set, "Possible multi-aggregation led to aggregation of variables <%s> and <%s> with scalars %g and %g and constant %g.\n",
5582 SCIPvarGetName(tmpvars[0]), SCIPvarGetName(tmpvars[1]), tmpscalars[0], tmpscalars[1], -tmpconstant);
5583
5584 SCIP_CALL( SCIPvarTryAggregateVars(set, blkmem, stat, transprob, origprob, primal, tree, reopt, lp,
5585 cliquetable, branchcand, eventfilter, eventqueue, tmpvars[0], tmpvars[1], tmpscalars[0],
5586 tmpscalars[1], -tmpconstant, infeasible, aggregated) );
5587
5588 goto TERMINATE;
5589 }
5590 else
5591 /* @todo: it is possible to multi-aggregate another variable, does it make sense?,
5592 * rest looks like 0 = a_1*y_1 + ... + a_n*y_n + c and has at least three variables
5593 */
5594 goto TERMINATE;
5595 }
5596 /* this means that x = b*x + a_1*y_1 + ... + a_n*y_n + c */
5597 else if( !SCIPsetIsZero(set, tmpscalar) )
5598 {
5599 tmpscalar = 1 - tmpscalar;
5600 tmpconstant /= tmpscalar;
5601 for( v = ntmpvars - 1; v >= 0; --v )
5602 tmpscalars[v] /= tmpscalar;
5603 }
5604
5605 /* check, if we are in one of the simple cases */
5606 if( ntmpvars == 0 )
5607 {
5608 SCIPsetDebugMsg(set, "Possible multi-aggregation led to fixing of variable <%s> to %g.\n", SCIPvarGetName(var), tmpconstant);
5609 SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
5610 eventfilter, eventqueue, cliquetable, tmpconstant, infeasible, aggregated) );
5611 goto TERMINATE;
5612 }
5613
5614 /* if only one aggregation variable is left, we perform a normal aggregation instead of a multi-aggregation */
5615 if( ntmpvars == 1 )
5616 {
5617 SCIPsetDebugMsg(set, "Possible multi-aggregation led to aggregation of variables <%s> and <%s> with scalars %g and %g and constant %g.\n",
5618 SCIPvarGetName(var), SCIPvarGetName(tmpvars[0]), 1.0, -tmpscalars[0], tmpconstant);
5619
5620 SCIP_CALL( SCIPvarTryAggregateVars(set, blkmem, stat, transprob, origprob, primal, tree, reopt, lp,
5621 cliquetable, branchcand, eventfilter, eventqueue, var, tmpvars[0], 1.0, -tmpscalars[0], tmpconstant,
5622 infeasible, aggregated) );
5623
5624 goto TERMINATE;
5625 }
5626
5627 /**@todo currently we don't perform the multi aggregation if the multi aggregation variable has a non
5628 * empty hole list; this should be changed in the future */
5629 if( SCIPvarGetHolelistGlobal(var) != NULL )
5630 goto TERMINATE;
5631
5632 /* if the variable is not allowed to be multi-aggregated */
5633 if( SCIPvarDoNotMultaggr(var) )
5634 {
5635 SCIPsetDebugMsg(set, "variable is not allowed to be multi-aggregated.\n");
5636 goto TERMINATE;
5637 }
5638
5639 /* if the variable to be multi-aggregated has implications or variable bounds (i.e. is the implied variable or
5640 * variable bound variable of another variable), we have to remove it from the other variables implications or
5641 * variable bounds
5642 */
5643 SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, TRUE) );
5644 assert(var->vlbs == NULL);
5645 assert(var->vubs == NULL);
5646 assert(var->implics == NULL);
5647
5648 /* set the aggregated variable's objective value to 0.0 */
5649 obj = var->obj;
5650 SCIP_CALL( SCIPvarChgObj(var, blkmem, set, transprob, primal, lp, eventqueue, 0.0) );
5651
5652 /* since we change the variable type form loose to multi aggregated, we have to adjust the number of loose
5653 * variables in the LP data structure; the loose objective value (looseobjval) in the LP data structure, however,
5654 * gets adjusted automatically, due to the event SCIP_EVENTTYPE_OBJCHANGED which dropped in the moment where the
5655 * objective of this variable is set to zero
5656 */
5657 SCIPlpDecNLoosevars(lp);
5658
5659 /* unlock all rounding locks */
5660 for( i = 0; i < NLOCKTYPES; i++ )
5661 {
5662 nlocksdown[i] = var->nlocksdown[i];
5663 nlocksup[i] = var->nlocksup[i];
5664
5665 var->nlocksdown[i] = 0;
5666 var->nlocksup[i] = 0;
5667 }
5668
5669 /* convert variable into multi-aggregated variable */
5670 var->varstatus = SCIP_VARSTATUS_MULTAGGR; /*lint !e641*/
5671 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->data.multaggr.vars, tmpvars, ntmpvars) );
5672 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->data.multaggr.scalars, tmpscalars, ntmpvars) );
5673 var->data.multaggr.constant = tmpconstant;
5674 var->data.multaggr.nvars = ntmpvars;
5675 var->data.multaggr.varssize = ntmpvars;
5676
5677 /* mark variable to be non-deletable */
5678 SCIPvarMarkNotDeletable(var);
5679
5680 /* relock the variable, thus increasing the locks of the aggregation variables */
5681 for( i = 0; i < NLOCKTYPES; i++ )
5682 {
5683 SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, nlocksdown[i], nlocksup[i]) );
5684 }
5685
5686 /* update flags and branching factors and priorities of aggregation variables;
5687 * update preferred branching direction of all aggregation variables that don't have a preferred direction yet
5688 */
5689 branchfactor = var->branchfactor;
5690 branchpriority = var->branchpriority;
5691 branchdirection = (SCIP_BRANCHDIR)var->branchdirection;
5692
5693 for( v = 0; v < ntmpvars; ++v )
5694 {
5695 assert(tmpvars[v] != NULL);
5696 tmpvars[v]->removable &= var->removable;
5697 branchfactor = MAX(tmpvars[v]->branchfactor, branchfactor);
5698 branchpriority = MAX(tmpvars[v]->branchpriority, branchpriority);
5699
5700 /* mark variable to be non-deletable */
5701 SCIPvarMarkNotDeletable(tmpvars[v]);
5702 }
5703 for( v = 0; v < ntmpvars; ++v )
5704 {
5705 SCIP_CALL( SCIPvarChgBranchFactor(tmpvars[v], set, branchfactor) );
5706 SCIP_CALL( SCIPvarChgBranchPriority(tmpvars[v], branchpriority) );
5707 if( (SCIP_BRANCHDIR)tmpvars[v]->branchdirection == SCIP_BRANCHDIR_AUTO )
5708 {
5709 if( tmpscalars[v] >= 0.0 )
5710 {
5711 SCIP_CALL( SCIPvarChgBranchDirection(tmpvars[v], branchdirection) );
5712 }
5713 else
5714 {
5715 SCIP_CALL( SCIPvarChgBranchDirection(tmpvars[v], SCIPbranchdirOpposite(branchdirection)) );
5716 }
5717 }
5718 }
5719 SCIP_CALL( SCIPvarChgBranchFactor(var, set, branchfactor) );
5720 SCIP_CALL( SCIPvarChgBranchPriority(var, branchpriority) );
5721
5722 if( var->probindex != -1 )
5723 {
5724 /* inform problem about the variable's status change */
5725 SCIP_CALL( SCIPprobVarChangedStatus(transprob, blkmem, set, branchcand, cliquetable, var) );
5726 }
5727
5728 /* issue VARFIXED event */
5729 SCIP_CALL( varEventVarFixed(var, blkmem, set, eventqueue, 2) );
5730
5731 /* reset the objective value of the aggregated variable, thus adjusting the objective value of the aggregation
5732 * variables and the problem's objective offset
5733 */
5734 SCIP_CALL( SCIPvarAddObj(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, eventfilter, eventqueue, obj) );
5735
5736 *aggregated = TRUE;
5737
5738 TERMINATE:
5739 BMSfreeBlockMemoryArray(blkmem, &tmpscalars, tmpvarssize);
5740 BMSfreeBlockMemoryArray(blkmem, &tmpvars, tmpvarssize);
5741
5742 break;
5743
5744 case SCIP_VARSTATUS_COLUMN:
5745 SCIPerrorMessage("cannot multi-aggregate a column variable\n");
5746 return SCIP_INVALIDDATA;
5747
5748 case SCIP_VARSTATUS_FIXED:
5749 SCIPerrorMessage("cannot multi-aggregate a fixed variable\n");
5750 return SCIP_INVALIDDATA;
5751
5752 case SCIP_VARSTATUS_AGGREGATED:
5753 SCIPerrorMessage("cannot multi-aggregate an aggregated variable\n");
5754 return SCIP_INVALIDDATA;
5755
5756 case SCIP_VARSTATUS_MULTAGGR:
5757 SCIPerrorMessage("cannot multi-aggregate a multiple aggregated variable again\n");
5758 return SCIP_INVALIDDATA;
5759
5760 case SCIP_VARSTATUS_NEGATED:
5761 /* aggregate negation variable x in x' = offset - x, instead of aggregating x' directly:
5762 * x' = a_1*y_1 + ... + a_n*y_n + c -> x = offset - x' = offset - a_1*y_1 - ... - a_n*y_n - c
5763 */
5764 assert(SCIPsetIsZero(set, var->obj));
5765 assert(var->negatedvar != NULL);
5766 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
5767 assert(var->negatedvar->negatedvar == var);
5768
5769 /* switch the signs of the aggregation scalars */
5770 for( v = 0; v < naggvars; ++v )
5771 scalars[v] *= -1.0;
5772
5773 /* perform the multi aggregation on the negation variable */
5774 SCIP_CALL( SCIPvarMultiaggregate(var->negatedvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
5775 cliquetable, branchcand, eventfilter, eventqueue, naggvars, aggvars, scalars,
5776 var->data.negate.constant - constant, infeasible, aggregated) );
5777
5778 /* switch the signs of the aggregation scalars again, to reset them to their original values */
5779 for( v = 0; v < naggvars; ++v )
5780 scalars[v] *= -1.0;
5781 break;
5782
5783 default:
5784 SCIPerrorMessage("unknown variable status\n");
5785 return SCIP_INVALIDDATA;
5786 }
5787
5788 /* check multi-aggregation on debugging solution */
5789 if( *infeasible || *aggregated )
5790 SCIP_CALL( SCIPdebugCheckAggregation(set, var, aggvars, scalars, constant, naggvars) ); /*lint !e506 !e774*/
5791
5792 return SCIP_OKAY;
5793 }
5794
5795 /** transformed variables are resolved to their active, fixed, or multi-aggregated problem variable of a variable,
5796 * or for original variables the same variable is returned
5797 */
5798 static
5799 SCIP_VAR* varGetActiveVar(
5800 SCIP_VAR* var /**< problem variable */
5801 )
5802 {
5803 SCIP_VAR* retvar;
5804
5805 assert(var != NULL);
5806
5807 retvar = var;
5808
5809 SCIPdebugMessage("get active variable of <%s>\n", var->name);
5810
5811 while( TRUE ) /*lint !e716 */
5812 {
5813 assert(retvar != NULL);
5814
5815 switch( SCIPvarGetStatus(retvar) )
5816 {
5817 case SCIP_VARSTATUS_ORIGINAL:
5818 case SCIP_VARSTATUS_LOOSE:
5819 case SCIP_VARSTATUS_COLUMN:
5820 case SCIP_VARSTATUS_FIXED:
5821 return retvar;
5822
5823 case SCIP_VARSTATUS_MULTAGGR:
5824 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
5825 if ( retvar->data.multaggr.nvars == 1 )
5826 retvar = retvar->data.multaggr.vars[0];
5827 else
5828 return retvar;
5829 break;
5830
5831 case SCIP_VARSTATUS_AGGREGATED:
5832 retvar = retvar->data.aggregate.var;
5833 break;
5834
5835 case SCIP_VARSTATUS_NEGATED:
5836 retvar = retvar->negatedvar;
5837 break;
5838
5839 default:
5840 SCIPerrorMessage("unknown variable status\n");
5841 SCIPABORT();
5842 return NULL; /*lint !e527*/
5843 }
5844 }
5845 }
5846
5847 /** returns whether variable is not allowed to be aggregated */
5848 SCIP_Bool SCIPvarDoNotAggr(
5849 SCIP_VAR* var /**< problem variable */
5850 )
5851 {
5852 SCIP_VAR* retvar;
5853
5854 assert(var != NULL);
5855
5856 retvar = varGetActiveVar(var);
5857 assert(retvar != NULL);
5858
5859 switch( SCIPvarGetStatus(retvar) )
5860 {
5861 case SCIP_VARSTATUS_ORIGINAL:
5862 case SCIP_VARSTATUS_LOOSE:
5863 case SCIP_VARSTATUS_COLUMN:
5864 case SCIP_VARSTATUS_FIXED:
5865 return retvar->donotaggr;
5866
5867 case SCIP_VARSTATUS_MULTAGGR:
5868 return FALSE;
5869
5870 case SCIP_VARSTATUS_AGGREGATED:
5871 case SCIP_VARSTATUS_NEGATED:
5872 default:
5873 /* aggregated and negated variables should be resolved by varGetActiveVar() */
5874 SCIPerrorMessage("wrong variable status\n");
5875 SCIPABORT();
5876 return FALSE; /*lint !e527 */
5877 }
5878 }
5879
5880 /** returns whether variable is not allowed to be multi-aggregated */
5881 SCIP_Bool SCIPvarDoNotMultaggr(
5882 SCIP_VAR* var /**< problem variable */
5883 )
5884 {
5885 SCIP_VAR* retvar;
5886
5887 assert(var != NULL);
5888
5889 retvar = varGetActiveVar(var);
5890 assert(retvar != NULL);
5891
5892 switch( SCIPvarGetStatus(retvar) )
5893 {
5894 case SCIP_VARSTATUS_ORIGINAL:
5895 case SCIP_VARSTATUS_LOOSE:
5896 case SCIP_VARSTATUS_COLUMN:
5897 case SCIP_VARSTATUS_FIXED:
5898 return retvar->donotmultaggr;
5899
5900 case SCIP_VARSTATUS_MULTAGGR:
5901 return FALSE;
5902
5903 case SCIP_VARSTATUS_AGGREGATED:
5904 case SCIP_VARSTATUS_NEGATED:
5905 default:
5906 /* aggregated and negated variables should be resolved by varGetActiveVar() */
5907 SCIPerrorMessage("wrong variable status\n");
5908 SCIPABORT();
5909 return FALSE; /*lint !e527 */
5910 }
5911 }
5912
5913 /** gets negated variable x' = offset - x of problem variable x; the negated variable is created if not yet existing;
5914 * the negation offset of binary variables is always 1, the offset of other variables is fixed to lb + ub when the
5915 * negated variable is created
5916 */
5917 SCIP_RETCODE SCIPvarNegate(
5918 SCIP_VAR* var, /**< problem variable to negate */
5919 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
5920 SCIP_SET* set, /**< global SCIP settings */
5921 SCIP_STAT* stat, /**< problem statistics */
5922 SCIP_VAR** negvar /**< pointer to store the negated variable */
5923 )
5924 {
5925 assert(var != NULL);
5926 assert(var->scip == set->scip);
5927 assert(negvar != NULL);
5928
5929 /* check, if we already created the negated variable */
5930 if( var->negatedvar == NULL )
5931 {
5932 char negvarname[SCIP_MAXSTRLEN];
5933
5934 assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_NEGATED);
5935
5936 SCIPsetDebugMsg(set, "creating negated variable of <%s>\n", var->name);
5937
5938 /* negation is only possible for bounded variables */
5939 if( SCIPsetIsInfinity(set, -var->glbdom.lb) || SCIPsetIsInfinity(set, var->glbdom.ub) )
5940 {
5941 SCIPerrorMessage("cannot negate unbounded variable\n");
5942 return SCIP_INVALIDDATA;
5943 }
5944
5945 (void) SCIPsnprintf(negvarname, SCIP_MAXSTRLEN, "%s_neg", var->name);
5946
5947 /* create negated variable */
5948 SCIP_CALL( varCreate(negvar, blkmem, set, stat, negvarname, var->glbdom.lb, var->glbdom.ub, 0.0,
5949 SCIPvarGetType(var), var->initial, var->removable, NULL, NULL, NULL, NULL, NULL) );
5950 (*negvar)->varstatus = SCIP_VARSTATUS_NEGATED; /*lint !e641*/
5951 if( SCIPvarIsBinary(var) )
5952 (*negvar)->data.negate.constant = 1.0;
5953 else
5954 (*negvar)->data.negate.constant = var->glbdom.lb + var->glbdom.ub;
5955
5956 /* create event filter for transformed variable */
5957 if( SCIPvarIsTransformed(var) )
5958 {
5959 SCIP_CALL( SCIPeventfilterCreate(&(*negvar)->eventfilter, blkmem) );
5960 }
5961
5962 /* set the bounds corresponding to the negation variable */
5963 (*negvar)->glbdom.lb = (*negvar)->data.negate.constant - var->glbdom.ub;
5964 (*negvar)->glbdom.ub = (*negvar)->data.negate.constant - var->glbdom.lb;
5965 (*negvar)->locdom.lb = (*negvar)->data.negate.constant - var->locdom.ub;
5966 (*negvar)->locdom.ub = (*negvar)->data.negate.constant - var->locdom.lb;
5967 /**@todo create holes in the negated variable corresponding to the holes of the negation variable */
5968
5969 /* link the variables together */
5970 var->negatedvar = *negvar;
5971 (*negvar)->negatedvar = var;
5972
5973 /* mark both variables to be non-deletable */
5974 SCIPvarMarkNotDeletable(var);
5975 SCIPvarMarkNotDeletable(*negvar);
5976
5977 /* copy the branch factor and priority, and use the negative preferred branching direction */
5978 (*negvar)->branchfactor = var->branchfactor;
5979 (*negvar)->branchpriority = var->branchpriority;
5980 (*negvar)->branchdirection = SCIPbranchdirOpposite((SCIP_BRANCHDIR)var->branchdirection); /*lint !e641*/
5981
5982 /* copy donot(mult)aggr status */
5983 (*negvar)->donotaggr = var->donotaggr;
5984 (*negvar)->donotmultaggr = var->donotmultaggr;
5985
5986 /* copy lazy bounds (they have to be flipped) */
5987 (*negvar)->lazylb = (*negvar)->data.negate.constant - var->lazyub;
5988 (*negvar)->lazyub = (*negvar)->data.negate.constant - var->lazylb;
5989
5990 /* make negated variable a parent of the negation variable (negated variable is captured as a parent) */
5991 SCIP_CALL( varAddParent(var, blkmem, set, *negvar) );
5992 assert((*negvar)->nuses == 1);
5993 }
5994 assert(var->negatedvar != NULL);
5995
5996 /* return the negated variable */
5997 *negvar = var->negatedvar;
5998
5999 /* exactly one variable of the negation pair has to be marked as negated variable */
6000 assert((SCIPvarGetStatus(*negvar) == SCIP_VARSTATUS_NEGATED) != (SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED));
6001
6002 return SCIP_OKAY;
6003 }
6004
6005 /** informs variable that its position in problem's vars array changed */
6006 static
6007 void varSetProbindex(
6008 SCIP_VAR* var, /**< problem variable */
6009 int probindex /**< new problem index of variable (-1 for removal) */
6010 )
6011 {
6012 assert(var != NULL);
6013 assert(probindex >= 0 || var->vlbs == NULL);
6014 assert(probindex >= 0 || var->vubs == NULL);
6015 assert(probindex >= 0 || var->implics == NULL);
6016
6017 var->probindex = probindex;
6018 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
6019 {
6020 assert(var->data.col != NULL);
6021 var->data.col->var_probindex = probindex;
6022 }
6023 }
6024
6025 /** informs variable that its position in problem's vars array changed */
6026 void SCIPvarSetProbindex(
6027 SCIP_VAR* var, /**< problem variable */
6028 int probindex /**< new problem index of variable */
6029 )
6030 {
6031 assert(var != NULL);
6032 assert(probindex >= 0);
6033
6034 varSetProbindex(var, probindex);
6035 }
6036
6037 /** gives the variable a new name
6038 *
6039 * @note the old pointer is overwritten, which might result in a memory leakage
6040 */
6041 void SCIPvarSetNamePointer(
6042 SCIP_VAR* var, /**< problem variable */
6043 const char* name /**< new name of variable */
6044 )
6045 {
6046 assert(var != NULL);
6047 assert(name != NULL);
6048
6049 var->name = (char*)name;
6050 }
6051
6052 /** informs variable that it will be removed from the problem; adjusts probindex and removes variable from the
6053 * implication graph;
6054 * If 'final' is TRUE, the thorough implication graph removal is not performed. Instead, only the
6055 * variable bounds and implication data structures of the variable are freed. Since in the final removal
6056 * of all variables from the transformed problem, this deletes the implication graph completely and is faster
6057 * than removing the variables one by one, each time updating all lists of the other variables.
6058 */
6059 SCIP_RETCODE SCIPvarRemove(
6060 SCIP_VAR* var, /**< problem variable */
6061 BMS_BLKMEM* blkmem, /**< block memory buffer */
6062 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
6063 SCIP_SET* set, /**< global SCIP settings */
6064 SCIP_Bool final /**< is this the final removal of all problem variables? */
6065 )
6066 {
6067 assert(SCIPvarGetProbindex(var) >= 0);
6068 assert(var->scip == set->scip);
6069
6070 /* if the variable is active in the transformed problem, remove it from the implication graph */
6071 if( SCIPvarIsTransformed(var)
6072 && (SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN) )
6073 {
6074 if( final )
6075 {
6076 /* just destroy the data structures */
6077 SCIPvboundsFree(&var->vlbs, blkmem);
6078 SCIPvboundsFree(&var->vubs, blkmem);
6079 SCIPimplicsFree(&var->implics, blkmem);
6080 }
6081 else
6082 {
6083 /* unlink the variable from all other variables' lists and free the data structures */
6084 SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, TRUE) );
6085 }
6086 }
6087
6088 /* mark the variable to be no longer a member of the problem */
6089 varSetProbindex(var, -1);
6090
6091 return SCIP_OKAY;
6092 }
6093
6094 /** marks the variable to be deleted from the problem */
6095 void SCIPvarMarkDeleted(
6096 SCIP_VAR* var /**< problem variable */
6097 )
6098 {
6099 assert(var != NULL);
6100 assert(var->probindex != -1);
6101
6102 var->deleted = TRUE;
6103 }
6104
6105 /** marks the variable to not to be aggregated */
6106 SCIP_RETCODE SCIPvarMarkDoNotAggr(
6107 SCIP_VAR* var /**< problem variable */
6108 )
6109 {
6110 SCIP_VAR* retvar;
6111
6112 assert(var != NULL);
6113
6114 retvar = varGetActiveVar(var);
6115 assert(retvar != NULL);
6116
6117 switch( SCIPvarGetStatus(retvar) )
6118 {
6119 case SCIP_VARSTATUS_ORIGINAL:
6120 case SCIP_VARSTATUS_LOOSE:
6121 case SCIP_VARSTATUS_COLUMN:
6122 case SCIP_VARSTATUS_FIXED:
6123 retvar->donotaggr = TRUE;
6124 break;
6125
6126 case SCIP_VARSTATUS_MULTAGGR:
6127 SCIPerrorMessage("cannot mark a multi-aggregated variable to not be aggregated.\n");
6128 return SCIP_INVALIDDATA;
6129
6130 case SCIP_VARSTATUS_AGGREGATED:
6131 case SCIP_VARSTATUS_NEGATED:
6132 default:
6133 /* aggregated and negated variables should be resolved by varGetActiveVar() */
6134 SCIPerrorMessage("wrong variable status\n");
6135 return SCIP_INVALIDDATA;
6136 }
6137
6138 return SCIP_OKAY;
6139 }
6140
6141 /** marks the variable to not to be multi-aggregated */
6142 SCIP_RETCODE SCIPvarMarkDoNotMultaggr(
6143 SCIP_VAR* var /**< problem variable */
6144 )
6145 {
6146 SCIP_VAR* retvar;
6147
6148 assert(var != NULL);
6149
6150 retvar = varGetActiveVar(var);
6151 assert(retvar != NULL);
6152
6153 switch( SCIPvarGetStatus(retvar) )
6154 {
6155 case SCIP_VARSTATUS_ORIGINAL:
6156 case SCIP_VARSTATUS_LOOSE:
6157 case SCIP_VARSTATUS_COLUMN:
6158 case SCIP_VARSTATUS_FIXED:
6159 retvar->donotmultaggr = TRUE;
6160 break;
6161
6162 case SCIP_VARSTATUS_MULTAGGR:
6163 SCIPerrorMessage("cannot mark a multi-aggregated variable to not be multi-aggregated.\n");
6164 return SCIP_INVALIDDATA;
6165
6166 case SCIP_VARSTATUS_AGGREGATED:
6167 case SCIP_VARSTATUS_NEGATED:
6168 default:
6169 /* aggregated and negated variables should be resolved by varGetActiveVar() */
6170 SCIPerrorMessage("wrong variable status\n");
6171 return SCIP_INVALIDDATA;
6172 }
6173
6174 return SCIP_OKAY;
6175 }
6176
6177 /** changes type of variable; cannot be called, if var belongs to a problem */
6178 SCIP_RETCODE SCIPvarChgType(
6179 SCIP_VAR* var, /**< problem variable to change */
6180 BMS_BLKMEM* blkmem, /**< block memory */
6181 SCIP_SET* set, /**< global SCIP settings */
6182 SCIP_PRIMAL* primal, /**< primal data */
6183 SCIP_LP* lp, /**< current LP data */
6184 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6185 SCIP_VARTYPE vartype /**< new type of variable */
6186 )
6187 {
6188 SCIP_EVENT* event;
6189 SCIP_VARTYPE oldtype;
6190
6191 assert(var != NULL);
6192
6193 SCIPdebugMessage("change type of <%s> from %d to %d\n", var->name, SCIPvarGetType(var), vartype);
6194
6195 if( var->probindex >= 0 )
6196 {
6197 SCIPerrorMessage("cannot change type of variable already in the problem\n");
6198 return SCIP_INVALIDDATA;
6199 }
6200
6201 oldtype = (SCIP_VARTYPE)var->vartype;
6202 var->vartype = vartype; /*lint !e641*/
6203
6204 if( SCIPsetGetStage(set) > SCIP_STAGE_TRANSFORMING )
6205 {
6206 SCIP_CALL( SCIPeventCreateTypeChanged(&event, blkmem, var, oldtype, vartype) );
6207 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, primal, lp, NULL, NULL, &event) );
6208 }
6209
6210 if( var->negatedvar != NULL )
6211 {
6212 assert(oldtype == (SCIP_VARTYPE)var->negatedvar->vartype
6213 || SCIPvarIsBinary(var) == SCIPvarIsBinary(var->negatedvar));
6214
6215 var->negatedvar->vartype = vartype; /*lint !e641*/
6216
6217 if( SCIPsetGetStage(set) > SCIP_STAGE_TRANSFORMING )
6218 {
6219 SCIP_CALL( SCIPeventCreateTypeChanged(&event, blkmem, var->negatedvar, oldtype, vartype) );
6220 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, primal, lp, NULL, NULL, &event) );
6221 }
6222 }
6223
6224 return SCIP_OKAY;
6225 }
6226
6227 /** appends OBJCHANGED event to the event queue */
6228 static
6229 SCIP_RETCODE varEventObjChanged(
6230 SCIP_VAR* var, /**< problem variable to change */
6231 BMS_BLKMEM* blkmem, /**< block memory */
6232 SCIP_SET* set, /**< global SCIP settings */
6233 SCIP_PRIMAL* primal, /**< primal data */
6234 SCIP_LP* lp, /**< current LP data */
6235 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6236 SCIP_Real oldobj, /**< old objective value for variable */
6237 SCIP_Real newobj /**< new objective value for variable */
6238 )
6239 {
6240 SCIP_EVENT* event;
6241
6242 assert(var != NULL);
6243 assert(var->scip == set->scip);
6244 assert(var->eventfilter != NULL);
6245 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
6246 assert(SCIPvarIsTransformed(var));
6247
6248 /* In the case where the objcetive value of a variable is very close to epsilon, and it is aggregated
6249 * into a variable with a big objective value, round-off errors might make the assert oldobj != newobj fail.
6250 * Hence, we relax it by letting it pass if the variables are percieved the same and we use very large values
6251 * that make comparison with values close to epsilon inaccurate.
6252 */
6253 assert(!SCIPsetIsEQ(set, oldobj, newobj) ||
6254 (SCIPsetIsEQ(set, oldobj, newobj) && REALABS(newobj) > 1e+15 * SCIPsetEpsilon(set))
6255 );
6256
6257 SCIP_CALL( SCIPeventCreateObjChanged(&event, blkmem, var, oldobj, newobj) );
6258 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, primal, lp, NULL, NULL, &event) );
6259
6260 return SCIP_OKAY;
6261 }
6262
6263 /** changes objective value of variable */
6264 SCIP_RETCODE SCIPvarChgObj(
6265 SCIP_VAR* var, /**< variable to change */
6266 BMS_BLKMEM* blkmem, /**< block memory */
6267 SCIP_SET* set, /**< global SCIP settings */
6268 SCIP_PROB* prob, /**< problem data */
6269 SCIP_PRIMAL* primal, /**< primal data */
6270 SCIP_LP* lp, /**< current LP data */
6271 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6272 SCIP_Real newobj /**< new objective value for variable */
6273 )
6274 {
6275 SCIP_Real oldobj;
6276
6277 assert(var != NULL);
6278 assert(set != NULL);
6279 assert(var->scip == set->scip);
6280
6281 SCIPsetDebugMsg(set, "changing objective value of <%s> from %g to %g\n", var->name, var->obj, newobj);
6282
6283 if( !SCIPsetIsEQ(set, var->obj, newobj) )
6284 {
6285 switch( SCIPvarGetStatus(var) )
6286 {
6287 case SCIP_VARSTATUS_ORIGINAL:
6288 if( var->data.original.transvar != NULL )
6289 {
6290 assert(SCIPprobIsTransformed(prob));
6291
6292 SCIP_CALL( SCIPvarChgObj(var->data.original.transvar, blkmem, set, prob, primal, lp, eventqueue,
6293 (SCIP_Real) prob->objsense * newobj/prob->objscale) );
6294 }
6295 else
6296 assert(set->stage == SCIP_STAGE_PROBLEM);
6297
6298 var->obj = newobj;
6299 var->unchangedobj = newobj;
6300
6301 break;
6302
6303 case SCIP_VARSTATUS_LOOSE:
6304 case SCIP_VARSTATUS_COLUMN:
6305 oldobj = var->obj;
6306 var->obj = newobj;
6307
6308 /* update unchanged objective value of variable */
6309 if( !lp->divingobjchg )
6310 var->unchangedobj = newobj;
6311
6312 /* update the number of variables with non-zero objective coefficient;
6313 * we only want to do the update, if the variable is added to the problem;
6314 * since the objective of inactive variables cannot be changed, this corresponds to probindex != -1
6315 */
6316 if( SCIPvarIsActive(var) )
6317 SCIPprobUpdateNObjVars(prob, set, oldobj, var->obj);
6318
6319 SCIP_CALL( varEventObjChanged(var, blkmem, set, primal, lp, eventqueue, oldobj, var->obj) );
6320 break;
6321
6322 case SCIP_VARSTATUS_FIXED:
6323 case SCIP_VARSTATUS_AGGREGATED:
6324 case SCIP_VARSTATUS_MULTAGGR:
6325 case SCIP_VARSTATUS_NEGATED:
6326 SCIPerrorMessage("cannot change objective value of a fixed, aggregated, multi-aggregated, or negated variable\n");
6327 return SCIP_INVALIDDATA;
6328
6329 default:
6330 SCIPerrorMessage("unknown variable status\n");
6331 return SCIP_INVALIDDATA;
6332 }
6333 }
6334
6335 return SCIP_OKAY;
6336 }
6337
6338 /** adds value to objective value of variable */
6339 SCIP_RETCODE SCIPvarAddObj(
6340 SCIP_VAR* var, /**< variable to change */
6341 BMS_BLKMEM* blkmem, /**< block memory */
6342 SCIP_SET* set, /**< global SCIP settings */
6343 SCIP_STAT* stat, /**< problem statistics */
6344 SCIP_PROB* transprob, /**< transformed problem data */
6345 SCIP_PROB* origprob, /**< original problem data */
6346 SCIP_PRIMAL* primal, /**< primal data */
6347 SCIP_TREE* tree, /**< branch and bound tree */
6348 SCIP_REOPT* reopt, /**< reoptimization data structure */
6349 SCIP_LP* lp, /**< current LP data */
6350 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
6351 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6352 SCIP_Real addobj /**< additional objective value for variable */
6353 )
6354 {
6355 assert(var != NULL);
6356 assert(set != NULL);
6357 assert(var->scip == set->scip);
6358 assert(set->stage < SCIP_STAGE_INITSOLVE);
6359
6360 SCIPsetDebugMsg(set, "adding %g to objective value %g of <%s>\n", addobj, var->obj, var->name);
6361
6362 if( !SCIPsetIsZero(set, addobj) )
6363 {
6364 SCIP_Real oldobj;
6365 int i;
6366
6367 switch( SCIPvarGetStatus(var) )
6368 {
6369 case SCIP_VARSTATUS_ORIGINAL:
6370 if( var->data.original.transvar != NULL )
6371 {
6372 SCIP_CALL( SCIPvarAddObj(var->data.original.transvar, blkmem, set, stat, transprob, origprob, primal, tree,
6373 reopt, lp, eventfilter, eventqueue, (SCIP_Real) transprob->objsense * addobj/transprob->objscale) );
6374 }
6375 else
6376 assert(set->stage == SCIP_STAGE_PROBLEM);
6377
6378 var->obj += addobj;
6379 var->unchangedobj += addobj;
6380 assert(SCIPsetIsEQ(set, var->obj, var->unchangedobj));
6381
6382 break;
6383
6384 case SCIP_VARSTATUS_LOOSE:
6385 case SCIP_VARSTATUS_COLUMN:
6386 oldobj = var->obj;
6387 var->obj += addobj;
6388
6389 /* update unchanged objective value of variable */
6390 if( !lp->divingobjchg )
6391 {
6392 var->unchangedobj += addobj;
6393 assert(SCIPsetIsEQ(set, var->obj, var->unchangedobj));
6394 }
6395
6396 /* update the number of variables with non-zero objective coefficient;
6397 * we only want to do the update, if the variable is added to the problem;
6398 * since the objective of inactive variables cannot be changed, this corresponds to probindex != -1
6399 */
6400 if( SCIPvarIsActive(var) )
6401 SCIPprobUpdateNObjVars(transprob, set, oldobj, var->obj);
6402
6403 SCIP_CALL( varEventObjChanged(var, blkmem, set, primal, lp, eventqueue, oldobj, var->obj) );
6404 break;
6405
6406 case SCIP_VARSTATUS_FIXED:
6407 assert(SCIPsetIsEQ(set, var->locdom.lb, var->locdom.ub));
6408 SCIPprobAddObjoffset(transprob, var->locdom.lb * addobj);
6409 SCIP_CALL( SCIPprimalUpdateObjoffset(primal, blkmem, set, stat, eventfilter, eventqueue, transprob, origprob, tree, reopt, lp) );
6410 break;
6411
6412 case SCIP_VARSTATUS_AGGREGATED:
6413 assert(!var->donotaggr);
6414 /* x = a*y + c -> add a*addobj to obj. val. of y, and c*addobj to obj. offset of problem */
6415 SCIPprobAddObjoffset(transprob, var->data.aggregate.constant * addobj);
6416 SCIP_CALL( SCIPprimalUpdateObjoffset(primal, blkmem, set, stat, eventfilter, eventqueue, transprob, origprob, tree, reopt, lp) );
6417 SCIP_CALL( SCIPvarAddObj(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, primal, tree, reopt,
6418 lp, eventfilter, eventqueue, var->data.aggregate.scalar * addobj) );
6419 break;
6420
6421 case SCIP_VARSTATUS_MULTAGGR:
6422 assert(!var->donotmultaggr);
6423 /* 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 */
6424 SCIPprobAddObjoffset(transprob, var->data.multaggr.constant * addobj);
6425 SCIP_CALL( SCIPprimalUpdateObjoffset(primal, blkmem, set, stat, eventfilter, eventqueue, transprob, origprob, tree, reopt, lp) );
6426 for( i = 0; i < var->data.multaggr.nvars; ++i )
6427 {
6428 SCIP_CALL( SCIPvarAddObj(var->data.multaggr.vars[i], blkmem, set, stat, transprob, origprob, primal, tree,
6429 reopt, lp, eventfilter, eventqueue, var->data.multaggr.scalars[i] * addobj) );
6430 }
6431 break;
6432
6433 case SCIP_VARSTATUS_NEGATED:
6434 /* x' = offset - x -> add -addobj to obj. val. of x and offset*addobj to obj. offset of problem */
6435 assert(var->negatedvar != NULL);
6436 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
6437 assert(var->negatedvar->negatedvar == var);
6438 SCIPprobAddObjoffset(transprob, var->data.negate.constant * addobj);
6439 SCIP_CALL( SCIPprimalUpdateObjoffset(primal, blkmem, set, stat, eventfilter, eventqueue, transprob, origprob, tree, reopt, lp) );
6440 SCIP_CALL( SCIPvarAddObj(var->negatedvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
6441 eventfilter, eventqueue, -addobj) );
6442 break;
6443
6444 default:
6445 SCIPerrorMessage("unknown variable status\n");
6446 return SCIP_INVALIDDATA;
6447 }
6448 }
6449
6450 return SCIP_OKAY;
6451 }
6452
6453 /** changes objective value of variable in current dive */
6454 SCIP_RETCODE SCIPvarChgObjDive(
6455 SCIP_VAR* var, /**< problem variable to change */
6456 SCIP_SET* set, /**< global SCIP settings */
6457 SCIP_LP* lp, /**< current LP data */
6458 SCIP_Real newobj /**< new objective value for variable */
6459 )
6460 {
6461 assert(var != NULL);
6462 assert(set != NULL);
6463 assert(var->scip == set->scip);
6464 assert(lp != NULL);
6465
6466 SCIPsetDebugMsg(set, "changing objective of <%s> to %g in current dive\n", var->name, newobj);
6467
6468 if( SCIPsetIsZero(set, newobj) )
6469 newobj = 0.0;
6470
6471 /* change objective value of attached variables */
6472 switch( SCIPvarGetStatus(var) )
6473 {
6474 case SCIP_VARSTATUS_ORIGINAL:
6475 assert(var->data.original.transvar != NULL);
6476 SCIP_CALL( SCIPvarChgObjDive(var->data.original.transvar, set, lp, newobj) );
6477 break;
6478
6479 case SCIP_VARSTATUS_COLUMN:
6480 assert(var->data.col != NULL);
6481 SCIP_CALL( SCIPcolChgObj(var->data.col, set, lp, newobj) );
6482 break;
6483
6484 case SCIP_VARSTATUS_LOOSE:
6485 case SCIP_VARSTATUS_FIXED:
6486 /* nothing to do here: only the constant shift in objective function would change */
6487 break;
6488
6489 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
6490 assert(var->data.aggregate.var != NULL);
6491 assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
6492 SCIP_CALL( SCIPvarChgObjDive(var->data.aggregate.var, set, lp, newobj / var->data.aggregate.scalar) );
6493 /* the constant can be ignored, because it would only affect the objective shift */
6494 break;
6495
6496 case SCIP_VARSTATUS_MULTAGGR:
6497 SCIPerrorMessage("cannot change diving objective value of a multi-aggregated variable\n");
6498 return SCIP_INVALIDDATA;
6499
6500 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
6501 assert(var->negatedvar != NULL);
6502 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
6503 assert(var->negatedvar->negatedvar == var);
6504 SCIP_CALL( SCIPvarChgObjDive(var->negatedvar, set, lp, -newobj) );
6505 /* the offset can be ignored, because it would only affect the objective shift */
6506 break;
6507
6508 default:
6509 SCIPerrorMessage("unknown variable status\n");
6510 return SCIP_INVALIDDATA;
6511 }
6512
6513 return SCIP_OKAY;
6514 }
6515
6516 /** adjust lower bound to integral value, if variable is integral */
6517 void SCIPvarAdjustLb(
6518 SCIP_VAR* var, /**< problem variable */
6519 SCIP_SET* set, /**< global SCIP settings */
6520 SCIP_Real* lb /**< pointer to lower bound to adjust */
6521 )
6522 {
6523 assert(var != NULL);
6524 assert(set != NULL);
6525 assert(var->scip == set->scip);
6526 assert(lb != NULL);
6527
6528 SCIPsetDebugMsg(set, "adjust lower bound %g of <%s>\n", *lb, var->name);
6529
6530 *lb = adjustedLb(set, SCIPvarGetType(var), *lb);
6531 }
6532
6533 /** adjust upper bound to integral value, if variable is integral */
6534 void SCIPvarAdjustUb(
6535 SCIP_VAR* var, /**< problem variable */
6536 SCIP_SET* set, /**< global SCIP settings */
6537 SCIP_Real* ub /**< pointer to upper bound to adjust */
6538 )
6539 {
6540 assert(var != NULL);
6541 assert(set != NULL);
6542 assert(var->scip == set->scip);
6543 assert(ub != NULL);
6544
6545 SCIPsetDebugMsg(set, "adjust upper bound %g of <%s>\n", *ub, var->name);
6546
6547 *ub = adjustedUb(set, SCIPvarGetType(var), *ub);
6548 }
6549
6550 /** adjust lower or upper bound to integral value, if variable is integral */
6551 void SCIPvarAdjustBd(
6552 SCIP_VAR* var, /**< problem variable */
6553 SCIP_SET* set, /**< global SCIP settings */
6554 SCIP_BOUNDTYPE boundtype, /**< type of bound to adjust */
6555 SCIP_Real* bd /**< pointer to bound to adjust */
6556 )
6557 {
6558 assert(boundtype == SCIP_BOUNDTYPE_LOWER || boundtype == SCIP_BOUNDTYPE_UPPER);
6559
6560 if( boundtype == SCIP_BOUNDTYPE_LOWER )
6561 SCIPvarAdjustLb(var, set, bd);
6562 else
6563 SCIPvarAdjustUb(var, set, bd);
6564 }
6565
6566 /** changes lower bound of original variable in original problem */
6567 SCIP_RETCODE SCIPvarChgLbOriginal(
6568 SCIP_VAR* var, /**< problem variable to change */
6569 SCIP_SET* set, /**< global SCIP settings */
6570 SCIP_Real newbound /**< new bound for variable */
6571 )
6572 {
6573 int i;
6574
6575 assert(var != NULL);
6576 assert(!SCIPvarIsTransformed(var));
6577 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
6578 assert(set != NULL);
6579 assert(var->scip == set->scip);
6580 assert(set->stage == SCIP_STAGE_PROBLEM);
6581
6582 /* check that the bound is feasible */
6583 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsLE(set, newbound, SCIPvarGetUbOriginal(var)));
6584 /* adjust bound to integral value if variable is of integral type */
6585 newbound = adjustedLb(set, SCIPvarGetType(var), newbound);
6586
6587 if( SCIPsetIsZero(set, newbound) )
6588 newbound = 0.0;
6589
6590 /* original domains are only stored for ORIGINAL variables, not for NEGATED */
6591 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
6592 {
6593 SCIPsetDebugMsg(set, "changing original lower bound of <%s> from %g to %g\n",
6594 var->name, var->data.original.origdom.lb, newbound);
6595
6596 if( SCIPsetIsEQ(set, var->data.original.origdom.lb, newbound) )
6597 return SCIP_OKAY;
6598
6599 /* change the bound */
6600 var->data.original.origdom.lb = newbound;
6601 }
6602 else if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED )
6603 {
6604 assert( var->negatedvar != NULL );
6605 SCIP_CALL( SCIPvarChgUbOriginal(var->negatedvar, set, var->data.negate.constant - newbound) );
6606 }
6607
6608 /* process parent variables */
6609 for( i = 0; i < var->nparentvars; ++i )
6610 {
6611 SCIP_VAR* parentvar;
6612
6613 parentvar = var->parentvars[i];
6614 assert(parentvar != NULL);
6615 assert(SCIPvarGetStatus(parentvar) == SCIP_VARSTATUS_NEGATED);
6616 assert(parentvar->negatedvar == var);
6617 assert(var->negatedvar == parentvar);
6618
6619 SCIP_CALL( SCIPvarChgUbOriginal(parentvar, set, parentvar->data.negate.constant - newbound) );
6620 }
6621
6622 return SCIP_OKAY;
6623 }
6624
6625 /** changes upper bound of original variable in original problem */
6626 SCIP_RETCODE SCIPvarChgUbOriginal(
6627 SCIP_VAR* var, /**< problem variable to change */
6628 SCIP_SET* set, /**< global SCIP settings */
6629 SCIP_Real newbound /**< new bound for variable */
6630 )
6631 {
6632 int i;
6633
6634 assert(var != NULL);
6635 assert(!SCIPvarIsTransformed(var));
6636 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
6637 assert(set != NULL);
6638 assert(var->scip == set->scip);
6639 assert(set->stage == SCIP_STAGE_PROBLEM);
6640
6641 /* check that the bound is feasible */
6642 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsGE(set, newbound, SCIPvarGetLbOriginal(var)));
6643 /* adjust bound to integral value if variable is of integral type */
6644 newbound = adjustedUb(set, SCIPvarGetType(var), newbound);
6645
6646 if( SCIPsetIsZero(set, newbound) )
6647 newbound = 0.0;
6648
6649 /* original domains are only stored for ORIGINAL variables, not for NEGATED */
6650 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
6651 {
6652 SCIPsetDebugMsg(set, "changing original upper bound of <%s> from %g to %g\n",
6653 var->name, var->data.original.origdom.ub, newbound);
6654
6655 if( SCIPsetIsEQ(set, var->data.original.origdom.ub, newbound) )
6656 return SCIP_OKAY;
6657
6658 /* change the bound */
6659 var->data.original.origdom.ub = newbound;
6660 }
6661 else if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED )
6662 {
6663 assert( var->negatedvar != NULL );
6664 SCIP_CALL( SCIPvarChgLbOriginal(var->negatedvar, set, var->data.negate.constant - newbound) );
6665 }
6666
6667 /* process parent variables */
6668 for( i = 0; i < var->nparentvars; ++i )
6669 {
6670 SCIP_VAR* parentvar;
6671
6672 parentvar = var->parentvars[i];
6673 assert(parentvar != NULL);
6674 assert(SCIPvarGetStatus(parentvar) == SCIP_VARSTATUS_NEGATED);
6675 assert(parentvar->negatedvar == var);
6676 assert(var->negatedvar == parentvar);
6677
6678 SCIP_CALL( SCIPvarChgLbOriginal(parentvar, set, parentvar->data.negate.constant - newbound) );
6679 }
6680
6681 return SCIP_OKAY;
6682 }
6683
6684 /** appends GLBCHANGED event to the event queue */
6685 static
6686 SCIP_RETCODE varEventGlbChanged(
6687 SCIP_VAR* var, /**< problem variable to change */
6688 BMS_BLKMEM* blkmem, /**< block memory */
6689 SCIP_SET* set, /**< global SCIP settings */
6690 SCIP_LP* lp, /**< current LP data */
6691 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
6692 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6693 SCIP_Real oldbound, /**< old lower bound for variable */
6694 SCIP_Real newbound /**< new lower bound for variable */
6695 )
6696 {
6697 assert(var != NULL);
6698 assert(var->eventfilter != NULL);
6699 assert(SCIPvarIsTransformed(var));
6700 assert(!SCIPsetIsEQ(set, oldbound, newbound) || (newbound != oldbound && newbound * oldbound <= 0.0)); /*lint !e777*/
6701 assert(set != NULL);
6702 assert(var->scip == set->scip);
6703
6704 /* check, if the variable is being tracked for bound changes
6705 * COLUMN and LOOSE variables are tracked always, because global/root pseudo objective value has to be updated
6706 */
6707 if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_GLBCHANGED) != 0)
6708 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
6709 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
6710 {
6711 SCIP_EVENT* event;
6712
6713 SCIPsetDebugMsg(set, "issue GLBCHANGED event for variable <%s>: %g -> %g\n", var->name, oldbound, newbound);
6714
6715 SCIP_CALL( SCIPeventCreateGlbChanged(&event, blkmem, var, oldbound, newbound) );
6716 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
6717 }
6718
6719 return SCIP_OKAY;
6720 }
6721
6722 /** appends GUBCHANGED event to the event queue */
6723 static
6724 SCIP_RETCODE varEventGubChanged(
6725 SCIP_VAR* var, /**< problem variable to change */
6726 BMS_BLKMEM* blkmem, /**< block memory */
6727 SCIP_SET* set, /**< global SCIP settings */
6728 SCIP_LP* lp, /**< current LP data */
6729 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
6730 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6731 SCIP_Real oldbound, /**< old lower bound for variable */
6732 SCIP_Real newbound /**< new lower bound for variable */
6733 )
6734 {
6735 assert(var != NULL);
6736 assert(var->eventfilter != NULL);
6737 assert(SCIPvarIsTransformed(var));
6738 assert(!SCIPsetIsEQ(set, oldbound, newbound) || (newbound != oldbound && newbound * oldbound <= 0.0)); /*lint !e777*/
6739 assert(set != NULL);
6740 assert(var->scip == set->scip);
6741
6742 /* check, if the variable is being tracked for bound changes
6743 * COLUMN and LOOSE variables are tracked always, because global/root pseudo objective value has to be updated
6744 */
6745 if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_GUBCHANGED) != 0)
6746 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
6747 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
6748 {
6749 SCIP_EVENT* event;
6750
6751 SCIPsetDebugMsg(set, "issue GUBCHANGED event for variable <%s>: %g -> %g\n", var->name, oldbound, newbound);
6752
6753 SCIP_CALL( SCIPeventCreateGubChanged(&event, blkmem, var, oldbound, newbound) );
6754 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
6755 }
6756
6757 return SCIP_OKAY;
6758 }
6759
6760 /** appends GHOLEADDED event to the event queue */
6761 static
6762 SCIP_RETCODE varEventGholeAdded(
6763 SCIP_VAR* var, /**< problem variable to change */
6764 BMS_BLKMEM* blkmem, /**< block memory */
6765 SCIP_SET* set, /**< global SCIP settings */
6766 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6767 SCIP_Real left, /**< left bound of open interval in new hole */
6768 SCIP_Real right /**< right bound of open interval in new hole */
6769 )
6770 {
6771 assert(var != NULL);
6772 assert(var->eventfilter != NULL);
6773 assert(SCIPvarIsTransformed(var));
6774 assert(set != NULL);
6775 assert(var->scip == set->scip);
6776 assert(SCIPsetIsLT(set, left, right));
6777
6778 /* check, if the variable is being tracked for bound changes */
6779 if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_GHOLEADDED) != 0) )
6780 {
6781 SCIP_EVENT* event;
6782
6783 SCIPsetDebugMsg(set, "issue GHOLEADDED event for variable <%s>: (%.15g,%.15g)\n", var->name, left, right);
6784
6785 SCIP_CALL( SCIPeventCreateGholeAdded(&event, blkmem, var, left, right) );
6786 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, NULL, &event) );
6787 }
6788
6789 return SCIP_OKAY;
6790 }
6791
6792 /** increases root bound change statistics after a global bound change */
6793 static
6794 void varIncRootboundchgs(
6795 SCIP_VAR* var, /**< problem variable to change */
6796 SCIP_SET* set, /**< global SCIP settings */
6797 SCIP_STAT* stat /**< problem statistics */
6798 )
6799 {
6800 assert(var != NULL);
6801 assert(set != NULL);
6802 assert(var->scip == set->scip);
6803 assert(stat != NULL);
6804
6805 if( SCIPvarIsActive(var) && SCIPvarIsTransformed(var) && set->stage == SCIP_STAGE_SOLVING )
6806 {
6807 stat->nrootboundchgs++;
6808 stat->nrootboundchgsrun++;
6809 if( SCIPvarIsIntegral(var) && SCIPvarGetLbGlobal(var) + 0.5 > SCIPvarGetUbGlobal(var) )
6810 {
6811 stat->nrootintfixings++;
6812 stat->nrootintfixingsrun++;
6813 }
6814 }
6815 }
6816
6817 /* forward declaration, because both methods call each other recursively */
6818
6819 /* performs the current change in upper bound, changes all parents accordingly */
6820 static
6821 SCIP_RETCODE varProcessChgUbGlobal(
6822 SCIP_VAR* var, /**< problem variable to change */
6823 BMS_BLKMEM* blkmem, /**< block memory */
6824 SCIP_SET* set, /**< global SCIP settings */
6825 SCIP_STAT* stat, /**< problem statistics */
6826 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
6827 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
6828 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
6829 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
6830 SCIP_Real newbound /**< new bound for variable */
6831 );
6832
6833 /** performs the current change in lower bound, changes all parents accordingly */
6834 static
6835 SCIP_RETCODE varProcessChgLbGlobal(
6836 SCIP_VAR* var, /**< problem variable to change */
6837 BMS_BLKMEM* blkmem, /**< block memory */
6838 SCIP_SET* set, /**< global SCIP settings */
6839 SCIP_STAT* stat, /**< problem statistics */
6840 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
6841 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
6842 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
6843 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
6844 SCIP_Real newbound /**< new bound for variable */
6845 )
6846 {
6847 SCIP_VAR* parentvar;
6848 SCIP_Real oldbound;
6849 int i;
6850
6851 assert(var != NULL);
6852 /* local domains can violate global bounds but not more than feasibility epsilon */
6853 assert(SCIPsetIsFeasLE(set, var->glbdom.lb, var->locdom.lb));
6854 assert(SCIPsetIsFeasLE(set, var->locdom.ub, var->glbdom.ub));
6855 assert(blkmem != NULL);
6856 assert(set != NULL);
6857 assert(var->scip == set->scip);
6858 assert(stat != NULL);
6859
6860 /* adjust bound to integral value if variable is of integral type */
6861 newbound = adjustedLb(set, SCIPvarGetType(var), newbound);
6862
6863 /* check that the bound is feasible */
6864 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && newbound > var->glbdom.ub )
6865 {
6866 /* due to numerics we only want to be feasible in feasibility tolerance */
6867 assert(SCIPsetIsFeasLE(set, newbound, var->glbdom.ub));
6868 newbound = var->glbdom.ub;
6869 }
6870 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
6871
6872 assert(var->vartype != SCIP_VARTYPE_BINARY || SCIPsetIsEQ(set, newbound, 0.0) || SCIPsetIsEQ(set, newbound, 1.0)); /*lint !e641*/
6873
6874 SCIPsetDebugMsg(set, "process changing global lower bound of <%s> from %f to %f\n", var->name, var->glbdom.lb, newbound);
6875
6876 if( SCIPsetIsEQ(set, newbound, var->glbdom.lb) && !(newbound != var->glbdom.lb && newbound * var->glbdom.lb <= 0.0) ) /*lint !e777*/
6877 return SCIP_OKAY;
6878
6879 /* check bound on debugging solution */
6880 SCIP_CALL( SCIPdebugCheckLbGlobal(set->scip, var, newbound) ); /*lint !e506 !e774*/
6881
6882 /* change the bound */
6883 oldbound = var->glbdom.lb;
6884 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsFeasLE(set, newbound, var->glbdom.ub));
6885 var->glbdom.lb = newbound;
6886 assert( SCIPsetIsFeasLE(set, var->glbdom.lb, var->locdom.lb) );
6887 assert( SCIPsetIsFeasLE(set, var->locdom.ub, var->glbdom.ub) );
6888
6889 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
6890 {
6891 /* merges overlapping holes into single holes, moves bounds respectively */
6892 domMerge(&var->glbdom, blkmem, set, &newbound, NULL);
6893 }
6894
6895 /* update the root bound changes counters */
6896 varIncRootboundchgs(var, set, stat);
6897
6898 /* update the lbchginfos array by replacing worse local bounds with the new global bound and changing the
6899 * redundant bound changes to be branching decisions
6900 */
6901 for( i = 0; i < var->nlbchginfos; ++i )
6902 {
6903 assert(var->lbchginfos[i].var == var);
6904
6905 if( var->lbchginfos[i].oldbound < var->glbdom.lb )
6906 {
6907 SCIPsetDebugMsg(set, " -> adjust lower bound change <%s>: %g -> %g due to new global lower bound %g\n",
6908 SCIPvarGetName(var), var->lbchginfos[i].oldbound, var->lbchginfos[i].newbound, var->glbdom.lb);
6909 var->lbchginfos[i].oldbound = var->glbdom.lb;
6910 if( SCIPsetIsLE(set, var->lbchginfos[i].newbound, var->glbdom.lb) )
6911 {
6912 /* this bound change is redundant due to the new global bound */
6913 var->lbchginfos[i].newbound = var->glbdom.lb;
6914 var->lbchginfos[i].boundchgtype = SCIP_BOUNDCHGTYPE_BRANCHING; /*lint !e641*/
6915 var->lbchginfos[i].redundant = TRUE;
6916 }
6917 else
6918 break; /* from now on, the remaining local bound changes are not redundant */
6919 }
6920 else
6921 break; /* from now on, the remaining local bound changes are not redundant */
6922 }
6923
6924 /* remove redundant implications and variable bounds */
6925 if( (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE)
6926 && (!set->reopt_enable || set->stage == SCIP_STAGE_PRESOLVING) )
6927 {
6928 SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, TRUE, TRUE) );
6929 }
6930
6931 /* issue bound change event */
6932 assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
6933 if( var->eventfilter != NULL )
6934 {
6935 SCIP_CALL( varEventGlbChanged(var, blkmem, set, lp, branchcand, eventqueue, oldbound, newbound) );
6936 }
6937
6938 /* process parent variables */
6939 for( i = 0; i < var->nparentvars; ++i )
6940 {
6941 parentvar = var->parentvars[i];
6942 assert(parentvar != NULL);
6943
6944 switch( SCIPvarGetStatus(parentvar) )
6945 {
6946 case SCIP_VARSTATUS_ORIGINAL:
6947 SCIP_CALL( varProcessChgLbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
6948 break;
6949
6950 case SCIP_VARSTATUS_COLUMN:
6951 case SCIP_VARSTATUS_LOOSE:
6952 case SCIP_VARSTATUS_FIXED:
6953 case SCIP_VARSTATUS_MULTAGGR:
6954 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
6955 return SCIP_INVALIDDATA;
6956
6957 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
6958 assert(parentvar->data.aggregate.var == var);
6959 if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
6960 {
6961 SCIP_Real parentnewbound;
6962
6963 /* a > 0 -> change lower bound of y */
6964 assert(SCIPsetIsInfinity(set, -parentvar->glbdom.lb) || SCIPsetIsInfinity(set, -oldbound)
6965 || SCIPsetIsFeasEQ(set, parentvar->glbdom.lb, oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant)
6966 || (SCIPsetIsZero(set, parentvar->glbdom.lb / parentvar->data.aggregate.scalar) && SCIPsetIsZero(set, oldbound)));
6967
6968 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
6969 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
6970 else
6971 parentnewbound = newbound;
6972 SCIP_CALL( varProcessChgLbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, parentnewbound) );
6973 }
6974 else
6975 {
6976 SCIP_Real parentnewbound;
6977
6978 /* a < 0 -> change upper bound of y */
6979 assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
6980 assert(SCIPsetIsInfinity(set, parentvar->glbdom.ub) || SCIPsetIsInfinity(set, -oldbound)
6981 || SCIPsetIsFeasEQ(set, parentvar->glbdom.ub, oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant)
6982 || (SCIPsetIsZero(set, parentvar->glbdom.ub / parentvar->data.aggregate.scalar) && SCIPsetIsZero(set, oldbound)));
6983
6984 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
6985 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
6986 else
6987 parentnewbound = -newbound;
6988 SCIP_CALL( varProcessChgUbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, parentnewbound) );
6989 }
6990 break;
6991
6992 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
6993 assert(parentvar->negatedvar != NULL);
6994 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
6995 assert(parentvar->negatedvar->negatedvar == parentvar);
6996 SCIP_CALL( varProcessChgUbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
6997 parentvar->data.negate.constant - newbound) );
6998 break;
6999
7000 default:
7001 SCIPerrorMessage("unknown variable status\n");
7002 return SCIP_INVALIDDATA;
7003 }
7004 }
7005
7006 return SCIP_OKAY;
7007 }
7008
7009 /** performs the current change in upper bound, changes all parents accordingly */
7010 static
7011 SCIP_RETCODE varProcessChgUbGlobal(
7012 SCIP_VAR* var, /**< problem variable to change */
7013 BMS_BLKMEM* blkmem, /**< block memory */
7014 SCIP_SET* set, /**< global SCIP settings */
7015 SCIP_STAT* stat, /**< problem statistics */
7016 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7017 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7018 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7019 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
7020 SCIP_Real newbound /**< new bound for variable */
7021 )
7022 {
7023 SCIP_VAR* parentvar;
7024 SCIP_Real oldbound;
7025 int i;
7026
7027 assert(var != NULL);
7028 /* local domains can violate global bounds but not more than feasibility epsilon */
7029 assert(SCIPsetIsFeasLE(set, var->glbdom.lb , var->locdom.lb));
7030 assert(SCIPsetIsFeasLE(set, var->locdom.ub, var->glbdom.ub));
7031 assert(blkmem != NULL);
7032 assert(set != NULL);
7033 assert(var->scip == set->scip);
7034 assert(stat != NULL);
7035
7036 /* adjust bound to integral value if variable is of integral type */
7037 newbound = adjustedUb(set, SCIPvarGetType(var), newbound);
7038
7039 /* check that the bound is feasible */
7040 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && newbound < var->glbdom.lb )
7041 {
7042 /* due to numerics we only want to be feasible in feasibility tolerance */
7043 assert(SCIPsetIsFeasGE(set, newbound, var->glbdom.lb));
7044 newbound = var->glbdom.lb;
7045 }
7046 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
7047
7048 assert(var->vartype != SCIP_VARTYPE_BINARY || SCIPsetIsEQ(set, newbound, 0.0) || SCIPsetIsEQ(set, newbound, 1.0)); /*lint !e641*/
7049
7050 SCIPsetDebugMsg(set, "process changing global upper bound of <%s> from %f to %f\n", var->name, var->glbdom.ub, newbound);
7051
7052 if( SCIPsetIsEQ(set, newbound, var->glbdom.ub) && !(newbound != var->glbdom.ub && newbound * var->glbdom.ub <= 0.0) ) /*lint !e777*/
7053 return SCIP_OKAY;
7054
7055 /* check bound on debugging solution */
7056 SCIP_CALL( SCIPdebugCheckUbGlobal(set->scip, var, newbound) ); /*lint !e506 !e774*/
7057
7058 /* change the bound */
7059 oldbound = var->glbdom.ub;
7060 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsFeasGE(set, newbound, var->glbdom.lb));
7061 var->glbdom.ub = newbound;
7062 assert( SCIPsetIsFeasLE(set, var->glbdom.lb, var->locdom.lb) );
7063 assert( SCIPsetIsFeasLE(set, var->locdom.ub, var->glbdom.ub) );
7064
7065 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7066 {
7067 /* merges overlapping holes into single holes, moves bounds respectively */
7068 domMerge(&var->glbdom, blkmem, set, NULL, &newbound);
7069 }
7070
7071 /* update the root bound changes counters */
7072 varIncRootboundchgs(var, set, stat);
7073
7074 /* update the ubchginfos array by replacing worse local bounds with the new global bound and changing the
7075 * redundant bound changes to be branching decisions
7076 */
7077 for( i = 0; i < var->nubchginfos; ++i )
7078 {
7079 assert(var->ubchginfos[i].var == var);
7080 if( var->ubchginfos[i].oldbound > var->glbdom.ub )
7081 {
7082 SCIPsetDebugMsg(set, " -> adjust upper bound change <%s>: %g -> %g due to new global upper bound %g\n",
7083 SCIPvarGetName(var), var->ubchginfos[i].oldbound, var->ubchginfos[i].newbound, var->glbdom.ub);
7084 var->ubchginfos[i].oldbound = var->glbdom.ub;
7085 if( SCIPsetIsGE(set, var->ubchginfos[i].newbound, var->glbdom.ub) )
7086 {
7087 /* this bound change is redundant due to the new global bound */
7088 var->ubchginfos[i].newbound = var->glbdom.ub;
7089 var->ubchginfos[i].boundchgtype = SCIP_BOUNDCHGTYPE_BRANCHING; /*lint !e641*/
7090 var->ubchginfos[i].redundant = TRUE;
7091 }
7092 else
7093 break; /* from now on, the remaining local bound changes are not redundant */
7094 }
7095 else
7096 break; /* from now on, the remaining local bound changes are not redundant */
7097 }
7098
7099 /* remove redundant implications and variable bounds */
7100 if( (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE)
7101 && (!set->reopt_enable || set->stage == SCIP_STAGE_PRESOLVING) )
7102 {
7103 SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, TRUE, TRUE) );
7104 }
7105
7106 /* issue bound change event */
7107 assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
7108 if( var->eventfilter != NULL )
7109 {
7110 SCIP_CALL( varEventGubChanged(var, blkmem, set, lp, branchcand, eventqueue, oldbound, newbound) );
7111 }
7112
7113 /* process parent variables */
7114 for( i = 0; i < var->nparentvars; ++i )
7115 {
7116 parentvar = var->parentvars[i];
7117 assert(parentvar != NULL);
7118
7119 switch( SCIPvarGetStatus(parentvar) )
7120 {
7121 case SCIP_VARSTATUS_ORIGINAL:
7122 SCIP_CALL( varProcessChgUbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
7123 break;
7124
7125 case SCIP_VARSTATUS_COLUMN:
7126 case SCIP_VARSTATUS_LOOSE:
7127 case SCIP_VARSTATUS_FIXED:
7128 case SCIP_VARSTATUS_MULTAGGR:
7129 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
7130 return SCIP_INVALIDDATA;
7131
7132 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
7133 assert(parentvar->data.aggregate.var == var);
7134 if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
7135 {
7136 SCIP_Real parentnewbound;
7137
7138 /* a > 0 -> change upper bound of y */
7139 assert(SCIPsetIsInfinity(set, parentvar->glbdom.ub) || SCIPsetIsInfinity(set, oldbound)
7140 || SCIPsetIsFeasEQ(set, parentvar->glbdom.ub,
7141 oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant));
7142 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7143 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
7144 else
7145 parentnewbound = newbound;
7146 SCIP_CALL( varProcessChgUbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, parentnewbound) );
7147 }
7148 else
7149 {
7150 SCIP_Real parentnewbound;
7151
7152 /* a < 0 -> change lower bound of y */
7153 assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
7154 assert(SCIPsetIsInfinity(set, -parentvar->glbdom.lb) || SCIPsetIsInfinity(set, oldbound)
7155 || SCIPsetIsFeasEQ(set, parentvar->glbdom.lb,
7156 oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant));
7157 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7158 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
7159 else
7160 parentnewbound = -newbound;
7161 SCIP_CALL( varProcessChgLbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, parentnewbound) );
7162 }
7163 break;
7164
7165 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
7166 assert(parentvar->negatedvar != NULL);
7167 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
7168 assert(parentvar->negatedvar->negatedvar == parentvar);
7169 SCIP_CALL( varProcessChgLbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7170 parentvar->data.negate.constant - newbound) );
7171 break;
7172
7173 default:
7174 SCIPerrorMessage("unknown variable status\n");
7175 return SCIP_INVALIDDATA;
7176 }
7177 }
7178
7179 return SCIP_OKAY;
7180 }
7181
7182 /** changes global lower bound of variable; if possible, adjusts bound to integral value;
7183 * updates local lower bound if the global bound is tighter
7184 */
7185 SCIP_RETCODE SCIPvarChgLbGlobal(
7186 SCIP_VAR* var, /**< problem variable to change */
7187 BMS_BLKMEM* blkmem, /**< block memory */
7188 SCIP_SET* set, /**< global SCIP settings */
7189 SCIP_STAT* stat, /**< problem statistics */
7190 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7191 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7192 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7193 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
7194 SCIP_Real newbound /**< new bound for variable */
7195 )
7196 {
7197 assert(var != NULL);
7198 assert(blkmem != NULL);
7199 assert(set != NULL);
7200 assert(var->scip == set->scip);
7201
7202 /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
7203 * of the domain within feastol
7204 */
7205 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasGT(set, newbound, var->glbdom.ub));
7206
7207 /* adjust bound to integral value if variable is of integral type */
7208 newbound = adjustedLb(set, SCIPvarGetType(var), newbound);
7209
7210 /* check that the adjusted bound is feasible
7211 * @todo this does not have to be the case if the original problem was infeasible due to bounds and we are called
7212 * here because we reset bounds to their original value!
7213 */
7214 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasGT(set, newbound, var->glbdom.ub));
7215
7216 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7217 {
7218 /* we do not want to exceed the upperbound, which could have happened due to numerics */
7219 newbound = MIN(newbound, var->glbdom.ub);
7220 }
7221 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
7222
7223 /* the new global bound has to be tighter except we are in the original problem; this must be w.r.t. feastol because
7224 * SCIPvarFix() allows fixings that are outside of the domain within feastol
7225 */
7226 assert(lp == NULL || SCIPsetIsFeasLE(set, var->glbdom.lb, newbound) || (set->reopt_enable && set->stage == SCIP_STAGE_PRESOLVED));
7227
7228 SCIPsetDebugMsg(set, "changing global lower bound of <%s> from %g to %g\n", var->name, var->glbdom.lb, newbound);
7229
7230 if( SCIPsetIsEQ(set, var->glbdom.lb, newbound) && !(newbound != var->glbdom.lb && newbound * var->glbdom.lb <= 0.0) ) /*lint !e777*/
7231 return SCIP_OKAY;
7232
7233 /* change bounds of attached variables */
7234 switch( SCIPvarGetStatus(var) )
7235 {
7236 case SCIP_VARSTATUS_ORIGINAL:
7237 if( var->data.original.transvar != NULL )
7238 {
7239 SCIP_CALL( SCIPvarChgLbGlobal(var->data.original.transvar, blkmem, set, stat, lp, branchcand, eventqueue,
7240 cliquetable, newbound) );
7241 }
7242 else
7243 {
7244 assert(set->stage == SCIP_STAGE_PROBLEM);
7245 if( newbound > SCIPvarGetLbLocal(var) )
7246 {
7247 SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7248 }
7249 SCIP_CALL( varProcessChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
7250 }
7251 break;
7252
7253 case SCIP_VARSTATUS_COLUMN:
7254 case SCIP_VARSTATUS_LOOSE:
7255 if( newbound > SCIPvarGetLbLocal(var) )
7256 {
7257 SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7258 }
7259 SCIP_CALL( varProcessChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
7260 break;
7261
7262 case SCIP_VARSTATUS_FIXED:
7263 SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
7264 return SCIP_INVALIDDATA;
7265
7266 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
7267 assert(var->data.aggregate.var != NULL);
7268 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
7269 {
7270 SCIP_Real childnewbound;
7271
7272 /* a > 0 -> change lower bound of y */
7273 assert((SCIPsetIsInfinity(set, -var->glbdom.lb) && SCIPsetIsInfinity(set, -var->data.aggregate.var->glbdom.lb))
7274 || SCIPsetIsFeasEQ(set, var->glbdom.lb,
7275 var->data.aggregate.var->glbdom.lb * var->data.aggregate.scalar + var->data.aggregate.constant));
7276 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7277 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
7278 else
7279 childnewbound = newbound;
7280 SCIP_CALL( SCIPvarChgLbGlobal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7281 childnewbound) );
7282 }
7283 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
7284 {
7285 SCIP_Real childnewbound;
7286
7287 /* a < 0 -> change upper bound of y */
7288 assert((SCIPsetIsInfinity(set, -var->glbdom.lb) && SCIPsetIsInfinity(set, var->data.aggregate.var->glbdom.ub))
7289 || SCIPsetIsFeasEQ(set, var->glbdom.lb,
7290 var->data.aggregate.var->glbdom.ub * var->data.aggregate.scalar + var->data.aggregate.constant));
7291 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7292 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
7293 else
7294 childnewbound = -newbound;
7295 SCIP_CALL( SCIPvarChgUbGlobal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7296 childnewbound) );
7297 }
7298 else
7299 {
7300 SCIPerrorMessage("scalar is zero in aggregation\n");
7301 return SCIP_INVALIDDATA;
7302 }
7303 break;
7304
7305 case SCIP_VARSTATUS_MULTAGGR:
7306 SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
7307 return SCIP_INVALIDDATA;
7308
7309 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
7310 assert(var->negatedvar != NULL);
7311 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
7312 assert(var->negatedvar->negatedvar == var);
7313 SCIP_CALL( SCIPvarChgUbGlobal(var->negatedvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7314 var->data.negate.constant - newbound) );
7315 break;
7316
7317 default:
7318 SCIPerrorMessage("unknown variable status\n");
7319 return SCIP_INVALIDDATA;
7320 }
7321
7322 return SCIP_OKAY;
7323 }
7324
7325 /** changes global upper bound of variable; if possible, adjusts bound to integral value;
7326 * updates local upper bound if the global bound is tighter
7327 */
7328 SCIP_RETCODE SCIPvarChgUbGlobal(
7329 SCIP_VAR* var, /**< problem variable to change */
7330 BMS_BLKMEM* blkmem, /**< block memory */
7331 SCIP_SET* set, /**< global SCIP settings */
7332 SCIP_STAT* stat, /**< problem statistics */
7333 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7334 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7335 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7336 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
7337 SCIP_Real newbound /**< new bound for variable */
7338 )
7339 {
7340 assert(var != NULL);
7341 assert(blkmem != NULL);
7342 assert(set != NULL);
7343 assert(var->scip == set->scip);
7344
7345 /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
7346 * of the domain within feastol
7347 */
7348 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasLT(set, newbound, var->glbdom.lb));
7349
7350 /* adjust bound to integral value if variable is of integral type */
7351 newbound = adjustedUb(set, SCIPvarGetType(var), newbound);
7352
7353 /* check that the adjusted bound is feasible
7354 * @todo this does not have to be the case if the original problem was infeasible due to bounds and we are called
7355 * here because we reset bounds to their original value!
7356 */
7357 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasLT(set, newbound, var->glbdom.lb));
7358
7359 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7360 {
7361 /* we do not want to undercut the lowerbound, which could have happened due to numerics */
7362 newbound = MAX(newbound, var->glbdom.lb);
7363 }
7364 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
7365
7366 /* the new global bound has to be tighter except we are in the original problem; this must be w.r.t. feastol because
7367 * SCIPvarFix() allows fixings that are outside of the domain within feastol
7368 */
7369 assert(lp == NULL || SCIPsetIsFeasGE(set, var->glbdom.ub, newbound) || (set->reopt_enable && set->stage == SCIP_STAGE_PRESOLVED));
7370
7371 SCIPsetDebugMsg(set, "changing global upper bound of <%s> from %g to %g\n", var->name, var->glbdom.ub, newbound);
7372
7373 if( SCIPsetIsEQ(set, var->glbdom.ub, newbound) && !(newbound != var->glbdom.ub && newbound * var->glbdom.ub <= 0.0) ) /*lint !e777*/
7374 return SCIP_OKAY;
7375
7376 /* change bounds of attached variables */
7377 switch( SCIPvarGetStatus(var) )
7378 {
7379 case SCIP_VARSTATUS_ORIGINAL:
7380 if( var->data.original.transvar != NULL )
7381 {
7382 SCIP_CALL( SCIPvarChgUbGlobal(var->data.original.transvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7383 newbound) );
7384 }
7385 else
7386 {
7387 assert(set->stage == SCIP_STAGE_PROBLEM);
7388 if( newbound < SCIPvarGetUbLocal(var) )
7389 {
7390 SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7391 }
7392 SCIP_CALL( varProcessChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
7393 }
7394 break;
7395
7396 case SCIP_VARSTATUS_COLUMN:
7397 case SCIP_VARSTATUS_LOOSE:
7398 if( newbound < SCIPvarGetUbLocal(var) )
7399 {
7400 SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7401 }
7402 SCIP_CALL( varProcessChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
7403 break;
7404
7405 case SCIP_VARSTATUS_FIXED:
7406 SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
7407 return SCIP_INVALIDDATA;
7408
7409 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
7410 assert(var->data.aggregate.var != NULL);
7411 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
7412 {
7413 SCIP_Real childnewbound;
7414
7415 /* a > 0 -> change lower bound of y */
7416 assert((SCIPsetIsInfinity(set, var->glbdom.ub) && SCIPsetIsInfinity(set, var->data.aggregate.var->glbdom.ub))
7417 || SCIPsetIsFeasEQ(set, var->glbdom.ub,
7418 var->data.aggregate.var->glbdom.ub * var->data.aggregate.scalar + var->data.aggregate.constant));
7419 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7420 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
7421 else
7422 childnewbound = newbound;
7423 SCIP_CALL( SCIPvarChgUbGlobal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7424 childnewbound) );
7425 }
7426 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
7427 {
7428 SCIP_Real childnewbound;
7429
7430 /* a < 0 -> change upper bound of y */
7431 assert((SCIPsetIsInfinity(set, var->glbdom.ub) && SCIPsetIsInfinity(set, -var->data.aggregate.var->glbdom.lb))
7432 || SCIPsetIsFeasEQ(set, var->glbdom.ub,
7433 var->data.aggregate.var->glbdom.lb * var->data.aggregate.scalar + var->data.aggregate.constant));
7434 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7435 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
7436 else
7437 childnewbound = -newbound;
7438 SCIP_CALL( SCIPvarChgLbGlobal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7439 childnewbound) );
7440 }
7441 else
7442 {
7443 SCIPerrorMessage("scalar is zero in aggregation\n");
7444 return SCIP_INVALIDDATA;
7445 }
7446 break;
7447
7448 case SCIP_VARSTATUS_MULTAGGR:
7449 SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
7450 return SCIP_INVALIDDATA;
7451
7452 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
7453 assert(var->negatedvar != NULL);
7454 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
7455 assert(var->negatedvar->negatedvar == var);
7456 SCIP_CALL( SCIPvarChgLbGlobal(var->negatedvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7457 var->data.negate.constant - newbound) );
7458 break;
7459
7460 default:
7461 SCIPerrorMessage("unknown variable status\n");
7462 return SCIP_INVALIDDATA;
7463 }
7464
7465 return SCIP_OKAY;
7466 }
7467
7468 /** changes lazy lower bound of the variable, this is only possible if the variable is not in the LP yet */
7469 SCIP_RETCODE SCIPvarChgLbLazy(
7470 SCIP_VAR* var, /**< problem variable */
7471 SCIP_SET* set, /**< global SCIP settings */
7472 SCIP_Real lazylb /**< the lazy lower bound to be set */
7473 )
7474 {
7475 assert(var != NULL);
7476 assert(var->probindex != -1);
7477 assert(SCIPsetIsFeasGE(set, var->glbdom.ub, lazylb));
7478 assert(SCIPsetIsFeasGE(set, var->lazyub, lazylb));
7479 assert(set != NULL);
7480 assert(var->scip == set->scip);
7481
7482 /* variable should not be in the LP */
7483 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
7484 return SCIP_INVALIDCALL;
7485
7486 var->lazylb = lazylb;
7487
7488 return SCIP_OKAY;
7489 }
7490
7491 /** changes lazy upper bound of the variable, this is only possible if the variable is not in the LP yet */
7492 SCIP_RETCODE SCIPvarChgUbLazy(
7493 SCIP_VAR* var, /**< problem variable */
7494 SCIP_SET* set, /**< global SCIP settings */
7495 SCIP_Real lazyub /**< the lazy lower bound to be set */
7496 )
7497 {
7498 assert(var != NULL);
7499 assert(var->probindex != -1);
7500 assert(SCIPsetIsFeasGE(set, lazyub, var->glbdom.lb));
7501 assert(SCIPsetIsFeasGE(set, lazyub, var->lazylb));
7502 assert(set != NULL);
7503 assert(var->scip == set->scip);
7504
7505 /* variable should not be in the LP */
7506 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
7507 return SCIP_INVALIDCALL;
7508
7509 var->lazyub = lazyub;
7510
7511 return SCIP_OKAY;
7512 }
7513
7514
7515 /** changes global bound of variable; if possible, adjusts bound to integral value;
7516 * updates local bound if the global bound is tighter
7517 */
7518 SCIP_RETCODE SCIPvarChgBdGlobal(
7519 SCIP_VAR* var, /**< problem variable to change */
7520 BMS_BLKMEM* blkmem, /**< block memory */
7521 SCIP_SET* set, /**< global SCIP settings */
7522 SCIP_STAT* stat, /**< problem statistics */
7523 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7524 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7525 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7526 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
7527 SCIP_Real newbound, /**< new bound for variable */
7528 SCIP_BOUNDTYPE boundtype /**< type of bound: lower or upper bound */
7529 )
7530 {
7531 /* apply bound change to the LP data */
7532 switch( boundtype )
7533 {
7534 case SCIP_BOUNDTYPE_LOWER:
7535 return SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound);
7536 case SCIP_BOUNDTYPE_UPPER:
7537 return SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound);
7538 default:
7539 SCIPerrorMessage("unknown bound type\n");
7540 return SCIP_INVALIDDATA;
7541 }
7542 }
7543
7544 /** appends LBTIGHTENED or LBRELAXED event to the event queue */
7545 static
7546 SCIP_RETCODE varEventLbChanged(
7547 SCIP_VAR* var, /**< problem variable to change */
7548 BMS_BLKMEM* blkmem, /**< block memory */
7549 SCIP_SET* set, /**< global SCIP settings */
7550 SCIP_LP* lp, /**< current LP data */
7551 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
7552 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
7553 SCIP_Real oldbound, /**< old lower bound for variable */
7554 SCIP_Real newbound /**< new lower bound for variable */
7555 )
7556 {
7557 assert(var != NULL);
7558 assert(var->eventfilter != NULL);
7559 assert(SCIPvarIsTransformed(var));
7560 assert(!SCIPsetIsEQ(set, oldbound, newbound) || newbound == var->glbdom.lb || (newbound != oldbound && newbound * oldbound <= 0.0)); /*lint !e777*/
7561 assert(set != NULL);
7562 assert(var->scip == set->scip);
7563
7564 /* check, if the variable is being tracked for bound changes
7565 * COLUMN and LOOSE variables are tracked always, because row activities and LP changes have to be updated
7566 */
7567 if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_LBCHANGED) != 0)
7568 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
7569 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
7570 {
7571 SCIP_EVENT* event;
7572
7573 SCIPsetDebugMsg(set, "issue LBCHANGED event for variable <%s>: %g -> %g\n", var->name, oldbound, newbound);
7574
7575 SCIP_CALL( SCIPeventCreateLbChanged(&event, blkmem, var, oldbound, newbound) );
7576 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
7577 }
7578
7579 return SCIP_OKAY;
7580 }
7581
7582 /** appends UBTIGHTENED or UBRELAXED event to the event queue */
7583 static
7584 SCIP_RETCODE varEventUbChanged(
7585 SCIP_VAR* var, /**< problem variable to change */
7586 BMS_BLKMEM* blkmem, /**< block memory */
7587 SCIP_SET* set, /**< global SCIP settings */
7588 SCIP_LP* lp, /**< current LP data */
7589 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
7590 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
7591 SCIP_Real oldbound, /**< old upper bound for variable */
7592 SCIP_Real newbound /**< new upper bound for variable */
7593 )
7594 {
7595 assert(var != NULL);
7596 assert(var->eventfilter != NULL);
7597 assert(SCIPvarIsTransformed(var));
7598 assert(!SCIPsetIsEQ(set, oldbound, newbound) || newbound == var->glbdom.ub || (newbound != oldbound && newbound * oldbound <= 0.0)); /*lint !e777*/
7599 assert(set != NULL);
7600 assert(var->scip == set->scip);
7601
7602 /* check, if the variable is being tracked for bound changes
7603 * COLUMN and LOOSE variables are tracked always, because row activities and LP changes have to be updated
7604 */
7605 if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_UBCHANGED) != 0)
7606 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
7607 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
7608 {
7609 SCIP_EVENT* event;
7610
7611 SCIPsetDebugMsg(set, "issue UBCHANGED event for variable <%s>: %g -> %g\n", var->name, oldbound, newbound);
7612
7613 SCIP_CALL( SCIPeventCreateUbChanged(&event, blkmem, var, oldbound, newbound) );
7614 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
7615 }
7616
7617 return SCIP_OKAY;
7618 }
7619
7620 /* forward declaration, because both methods call each other recursively */
7621
7622 /* performs the current change in upper bound, changes all parents accordingly */
7623 static
7624 SCIP_RETCODE varProcessChgUbLocal(
7625 SCIP_VAR* var, /**< problem variable to change */
7626 BMS_BLKMEM* blkmem, /**< block memory */
7627 SCIP_SET* set, /**< global SCIP settings */
7628 SCIP_STAT* stat, /**< problem statistics, or NULL if the bound change belongs to updating the parent variables */
7629 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7630 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7631 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7632 SCIP_Real newbound /**< new bound for variable */
7633 );
7634
7635 /** performs the current change in lower bound, changes all parents accordingly */
7636 static
7637 SCIP_RETCODE varProcessChgLbLocal(
7638 SCIP_VAR* var, /**< problem variable to change */
7639 BMS_BLKMEM* blkmem, /**< block memory */
7640 SCIP_SET* set, /**< global SCIP settings */
7641 SCIP_STAT* stat, /**< problem statistics, or NULL if the bound change belongs to updating the parent variables */
7642 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7643 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7644 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7645 SCIP_Real newbound /**< new bound for variable */
7646 )
7647 {
7648 SCIP_VAR* parentvar;
7649 SCIP_Real oldbound;
7650 int i;
7651
7652 assert(var != NULL);
7653 assert(set != NULL);
7654 assert(var->scip == set->scip);
7655 assert((SCIPvarGetType(var) == SCIP_VARTYPE_BINARY && (SCIPsetIsZero(set, newbound) || SCIPsetIsEQ(set, newbound, 1.0)
7656 || SCIPsetIsEQ(set, newbound, var->locdom.ub)))
7657 || (SCIPvarGetType(var) < SCIP_VARTYPE_CONTINUOUS && (SCIPsetIsIntegral(set, newbound)
7658 || SCIPsetIsEQ(set, newbound, var->locdom.ub)))
7659 || SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS);
7660
7661 /* check that the bound is feasible */
7662 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsLE(set, newbound, var->glbdom.ub));
7663 /* adjust bound to integral value if variable is of integral type */
7664 newbound = adjustedLb(set, SCIPvarGetType(var), newbound);
7665
7666 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7667 {
7668 /* we do not want to exceed the upper bound, which could have happened due to numerics */
7669 newbound = MIN(newbound, var->locdom.ub);
7670
7671 /* we do not want to undercut the global lower bound, which could have happened due to numerics */
7672 newbound = MAX(newbound, var->glbdom.lb);
7673 }
7674 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
7675
7676 SCIPsetDebugMsg(set, "process changing lower bound of <%s> from %g to %g\n", var->name, var->locdom.lb, newbound);
7677
7678 if( SCIPsetIsEQ(set, newbound, var->glbdom.lb) && var->glbdom.lb != var->locdom.lb ) /*lint !e777*/
7679 newbound = var->glbdom.lb;
7680 else if( SCIPsetIsEQ(set, newbound, var->locdom.lb) && !(newbound != var->locdom.lb && newbound * var->locdom.lb <= 0.0) ) /*lint !e777*/
7681 return SCIP_OKAY;
7682
7683 /* change the bound */
7684 oldbound = var->locdom.lb;
7685 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsFeasLE(set, newbound, var->locdom.ub));
7686 var->locdom.lb = newbound;
7687
7688 /* update statistic; during the update steps of the parent variable we pass a NULL pointer to ensure that we only
7689 * once update the statistic
7690 */
7691 if( stat != NULL )
7692 SCIPstatIncrement(stat, set, domchgcount);
7693
7694 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7695 {
7696 /* merges overlapping holes into single holes, moves bounds respectively */
7697 domMerge(&var->locdom, blkmem, set, &newbound, NULL);
7698 }
7699
7700 /* issue bound change event */
7701 assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
7702 if( var->eventfilter != NULL )
7703 {
7704 SCIP_CALL( varEventLbChanged(var, blkmem, set, lp, branchcand, eventqueue, oldbound, newbound) );
7705 }
7706
7707 /* process parent variables */
7708 for( i = 0; i < var->nparentvars; ++i )
7709 {
7710 parentvar = var->parentvars[i];
7711 assert(parentvar != NULL);
7712
7713 switch( SCIPvarGetStatus(parentvar) )
7714 {
7715 case SCIP_VARSTATUS_ORIGINAL:
7716 SCIP_CALL( varProcessChgLbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, newbound) );
7717 break;
7718
7719 case SCIP_VARSTATUS_COLUMN:
7720 case SCIP_VARSTATUS_LOOSE:
7721 case SCIP_VARSTATUS_FIXED:
7722 case SCIP_VARSTATUS_MULTAGGR:
7723 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
7724 return SCIP_INVALIDDATA;
7725
7726 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
7727 assert(parentvar->data.aggregate.var == var);
7728 if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
7729 {
7730 SCIP_Real parentnewbound;
7731
7732 /* a > 0 -> change lower bound of y */
7733 assert(SCIPsetIsInfinity(set, -parentvar->locdom.lb) || SCIPsetIsInfinity(set, -oldbound)
7734 || SCIPsetIsFeasEQ(set, parentvar->locdom.lb, oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant)
7735 || (SCIPsetIsZero(set, parentvar->locdom.lb / parentvar->data.aggregate.scalar) && SCIPsetIsZero(set, oldbound)));
7736
7737 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7738 {
7739 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
7740 /* if parent's new lower bound exceeds its upper bound, then this could be due to numerical difficulties, e.g., if numbers are large
7741 * thus, at least a relative comparision of the new lower bound and the current upper bound should proof consistency
7742 * as a result, the parent's lower bound is set to it's upper bound, and not above
7743 */
7744 if( parentnewbound > parentvar->glbdom.ub )
7745 {
7746 /* due to numerics we only need to be feasible w.r.t. feasibility tolerance */
7747 assert(SCIPsetIsFeasLE(set, parentnewbound, parentvar->glbdom.ub));
7748 parentnewbound = parentvar->glbdom.ub;
7749 }
7750 }
7751 else
7752 parentnewbound = newbound;
7753 SCIP_CALL( varProcessChgLbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, parentnewbound) );
7754 }
7755 else
7756 {
7757 SCIP_Real parentnewbound;
7758
7759 /* a < 0 -> change upper bound of y */
7760 assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
7761 assert(SCIPsetIsInfinity(set, parentvar->locdom.ub) || SCIPsetIsInfinity(set, -oldbound)
7762 || SCIPsetIsFeasEQ(set, parentvar->locdom.ub, oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant)
7763 || (SCIPsetIsZero(set, parentvar->locdom.ub / parentvar->data.aggregate.scalar) && SCIPsetIsZero(set, oldbound)));
7764
7765 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7766 {
7767 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
7768 /* 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
7769 * thus, at least a relative comparision of the new upper bound and the current lower bound should proof consistency
7770 * as a result, the parent's upper bound is set to it's lower bound, and not below
7771 */
7772 if( parentnewbound < parentvar->glbdom.lb )
7773 {
7774 /* due to numerics we only need to be feasible w.r.t. feasibility tolerance */
7775 assert(SCIPsetIsFeasGE(set, parentnewbound, parentvar->glbdom.lb));
7776 parentnewbound = parentvar->glbdom.lb;
7777 }
7778 }
7779 else
7780 parentnewbound = -newbound;
7781 SCIP_CALL( varProcessChgUbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, parentnewbound) );
7782 }
7783 break;
7784
7785 case SCIP_VARSTATUS_NEGATED: /* x = offset - x' -> x' = offset - x */
7786 assert(parentvar->negatedvar != NULL);
7787 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
7788 assert(parentvar->negatedvar->negatedvar == parentvar);
7789 SCIP_CALL( varProcessChgUbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue,
7790 parentvar->data.negate.constant - newbound) );
7791 break;
7792
7793 default:
7794 SCIPerrorMessage("unknown variable status\n");
7795 return SCIP_INVALIDDATA;
7796 }
7797 }
7798
7799 return SCIP_OKAY;
7800 }
7801
7802 /** performs the current change in upper bound, changes all parents accordingly */
7803 static
7804 SCIP_RETCODE varProcessChgUbLocal(
7805 SCIP_VAR* var, /**< problem variable to change */
7806 BMS_BLKMEM* blkmem, /**< block memory */
7807 SCIP_SET* set, /**< global SCIP settings */
7808 SCIP_STAT* stat, /**< problem statistics, or NULL if the bound change belongs to updating the parent variables */
7809 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7810 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7811 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7812 SCIP_Real newbound /**< new bound for variable */
7813 )
7814 {
7815 SCIP_VAR* parentvar;
7816 SCIP_Real oldbound;
7817 int i;
7818
7819 assert(var != NULL);
7820 assert(set != NULL);
7821 assert(var->scip == set->scip);
7822 assert((SCIPvarGetType(var) == SCIP_VARTYPE_BINARY && (SCIPsetIsZero(set, newbound) || SCIPsetIsEQ(set, newbound, 1.0)
7823 || SCIPsetIsEQ(set, newbound, var->locdom.lb)))
7824 || (SCIPvarGetType(var) < SCIP_VARTYPE_CONTINUOUS && (SCIPsetIsIntegral(set, newbound)
7825 || SCIPsetIsEQ(set, newbound, var->locdom.lb)))
7826 || SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS);
7827
7828 /* check that the bound is feasible */
7829 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsGE(set, newbound, var->glbdom.lb));
7830 /* adjust bound to integral value if variable is of integral type */
7831 newbound = adjustedUb(set, SCIPvarGetType(var), newbound);
7832
7833 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7834 {
7835 /* we do not want to undercut the lower bound, which could have happened due to numerics */
7836 newbound = MAX(newbound, var->locdom.lb);
7837
7838 /* we do not want to exceed the global upper bound, which could have happened due to numerics */
7839 newbound = MIN(newbound, var->glbdom.ub);
7840 }
7841 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
7842
7843 SCIPsetDebugMsg(set, "process changing upper bound of <%s> from %g to %g\n", var->name, var->locdom.ub, newbound);
7844
7845 if( SCIPsetIsEQ(set, newbound, var->glbdom.ub) && var->glbdom.ub != var->locdom.ub ) /*lint !e777*/
7846 newbound = var->glbdom.ub;
7847 else if( SCIPsetIsEQ(set, newbound, var->locdom.ub) && !(newbound != var->locdom.ub && newbound * var->locdom.ub <= 0.0) ) /*lint !e777*/
7848 return SCIP_OKAY;
7849
7850 /* change the bound */
7851 oldbound = var->locdom.ub;
7852 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsFeasGE(set, newbound, var->locdom.lb));
7853 var->locdom.ub = newbound;
7854
7855 /* update statistic; during the update steps of the parent variable we pass a NULL pointer to ensure that we only
7856 * once update the statistic
7857 */
7858 if( stat != NULL )
7859 SCIPstatIncrement(stat, set, domchgcount);
7860
7861 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7862 {
7863 /* merges overlapping holes into single holes, moves bounds respectively */
7864 domMerge(&var->locdom, blkmem, set, NULL, &newbound);
7865 }
7866
7867 /* issue bound change event */
7868 assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
7869 if( var->eventfilter != NULL )
7870 {
7871 SCIP_CALL( varEventUbChanged(var, blkmem, set, lp, branchcand, eventqueue, oldbound, newbound) );
7872 }
7873
7874 /* process parent variables */
7875 for( i = 0; i < var->nparentvars; ++i )
7876 {
7877 parentvar = var->parentvars[i];
7878 assert(parentvar != NULL);
7879
7880 switch( SCIPvarGetStatus(parentvar) )
7881 {
7882 case SCIP_VARSTATUS_ORIGINAL:
7883 SCIP_CALL( varProcessChgUbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, newbound) );
7884 break;
7885
7886 case SCIP_VARSTATUS_COLUMN:
7887 case SCIP_VARSTATUS_LOOSE:
7888 case SCIP_VARSTATUS_FIXED:
7889 case SCIP_VARSTATUS_MULTAGGR:
7890 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
7891 return SCIP_INVALIDDATA;
7892
7893 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
7894 assert(parentvar->data.aggregate.var == var);
7895 if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
7896 {
7897 SCIP_Real parentnewbound;
7898
7899 /* a > 0 -> change upper bound of x */
7900 assert(SCIPsetIsInfinity(set, parentvar->locdom.ub) || SCIPsetIsInfinity(set, oldbound)
7901 || SCIPsetIsFeasEQ(set, parentvar->locdom.ub,
7902 oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant));
7903 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7904 {
7905 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
7906 /* 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
7907 * thus, at least a relative comparision of the new upper bound and the current lower bound should proof consistency
7908 * as a result, the parent's upper bound is set to it's lower bound, and not below
7909 */
7910 if( parentnewbound < parentvar->glbdom.lb )
7911 {
7912 /* due to numerics we only need to be feasible w.r.t. feasibility tolerance */
7913 assert(SCIPsetIsFeasGE(set, parentnewbound, parentvar->glbdom.lb));
7914 parentnewbound = parentvar->glbdom.lb;
7915 }
7916 }
7917 else
7918 parentnewbound = newbound;
7919 SCIP_CALL( varProcessChgUbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, parentnewbound) );
7920 }
7921 else
7922 {
7923 SCIP_Real parentnewbound;
7924
7925 /* a < 0 -> change lower bound of x */
7926 assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
7927 assert(SCIPsetIsInfinity(set, -parentvar->locdom.lb) || SCIPsetIsInfinity(set, oldbound)
7928 || SCIPsetIsFeasEQ(set, parentvar->locdom.lb,
7929 oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant));
7930 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7931 {
7932 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
7933 /* if parent's new lower bound exceeds its upper bound, then this could be due to numerical difficulties, e.g., if numbers are large
7934 * thus, at least a relative comparision of the new lower bound and the current upper bound should proof consistency
7935 * as a result, the parent's lower bound is set to it's upper bound, and not above
7936 */
7937 if( parentnewbound > parentvar->glbdom.ub )
7938 {
7939 /* due to numerics we only need to be feasible w.r.t. feasibility tolerance */
7940 assert(SCIPsetIsFeasLE(set, parentnewbound, parentvar->glbdom.ub));
7941 parentnewbound = parentvar->glbdom.ub;
7942 }
7943 }
7944 else
7945 parentnewbound = -newbound;
7946 SCIP_CALL( varProcessChgLbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, parentnewbound) );
7947 }
7948 break;
7949
7950 case SCIP_VARSTATUS_NEGATED: /* x = offset - x' -> x' = offset - x */
7951 assert(parentvar->negatedvar != NULL);
7952 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
7953 assert(parentvar->negatedvar->negatedvar == parentvar);
7954 SCIP_CALL( varProcessChgLbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue,
7955 parentvar->data.negate.constant - newbound) );
7956 break;
7957
7958 default:
7959 SCIPerrorMessage("unknown variable status\n");
7960 return SCIP_INVALIDDATA;
7961 }
7962 }
7963
7964 return SCIP_OKAY;
7965 }
7966
7967 /** changes current local lower bound of variable; if possible, adjusts bound to integral value; stores inference
7968 * information in variable
7969 */
7970 SCIP_RETCODE SCIPvarChgLbLocal(
7971 SCIP_VAR* var, /**< problem variable to change */
7972 BMS_BLKMEM* blkmem, /**< block memory */
7973 SCIP_SET* set, /**< global SCIP settings */
7974 SCIP_STAT* stat, /**< problem statistics */
7975 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7976 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7977 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7978 SCIP_Real newbound /**< new bound for variable */
7979 )
7980 {
7981 assert(var != NULL);
7982 assert(blkmem != NULL);
7983 assert(set != NULL);
7984 assert(var->scip == set->scip);
7985
7986 /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
7987 * of the domain within feastol
7988 */
7989 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasGT(set, newbound, var->locdom.ub));
7990
7991 /* adjust bound to integral value if variable is of integral type */
7992 newbound = adjustedLb(set, SCIPvarGetType(var), newbound);
7993
7994 /* check that the adjusted bound is feasible */
7995 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasGT(set, newbound, var->locdom.ub));
7996
7997 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7998 {
7999 /* we do not want to exceed the upperbound, which could have happened due to numerics */
8000 newbound = MIN(newbound, var->locdom.ub);
8001 }
8002 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
8003
8004 SCIPsetDebugMsg(set, "changing lower bound of <%s>[%g,%g] to %g\n", var->name, var->locdom.lb, var->locdom.ub, newbound);
8005
8006 if( SCIPsetIsEQ(set, var->locdom.lb, newbound) && (!SCIPsetIsEQ(set, var->glbdom.lb, newbound) || var->locdom.lb == newbound) /*lint !e777*/
8007 && !(newbound != var->locdom.lb && newbound * var->locdom.lb <= 0.0) ) /*lint !e777*/
8008 return SCIP_OKAY;
8009
8010 /* change bounds of attached variables */
8011 switch( SCIPvarGetStatus(var) )
8012 {
8013 case SCIP_VARSTATUS_ORIGINAL:
8014 if( var->data.original.transvar != NULL )
8015 {
8016 SCIP_CALL( SCIPvarChgLbLocal(var->data.original.transvar, blkmem, set, stat, lp, branchcand, eventqueue,
8017 newbound) );
8018 }
8019 else
8020 {
8021 assert(set->stage == SCIP_STAGE_PROBLEM);
8022 SCIP_CALL( varProcessChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
8023 }
8024 break;
8025
8026 case SCIP_VARSTATUS_COLUMN:
8027 case SCIP_VARSTATUS_LOOSE:
8028 SCIP_CALL( varProcessChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
8029 break;
8030
8031 case SCIP_VARSTATUS_FIXED:
8032 SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
8033 return SCIP_INVALIDDATA;
8034
8035 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
8036 assert(var->data.aggregate.var != NULL);
8037 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
8038 {
8039 SCIP_Real childnewbound;
8040
8041 /* a > 0 -> change lower bound of y */
8042 assert((SCIPsetIsInfinity(set, -var->locdom.lb) && SCIPsetIsInfinity(set, -var->data.aggregate.var->locdom.lb))
8043 || SCIPsetIsFeasEQ(set, var->locdom.lb,
8044 var->data.aggregate.var->locdom.lb * var->data.aggregate.scalar + var->data.aggregate.constant));
8045 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
8046 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
8047 else
8048 childnewbound = newbound;
8049 SCIP_CALL( SCIPvarChgLbLocal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue,
8050 childnewbound) );
8051 }
8052 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
8053 {
8054 SCIP_Real childnewbound;
8055
8056 /* a < 0 -> change upper bound of y */
8057 assert((SCIPsetIsInfinity(set, -var->locdom.lb) && SCIPsetIsInfinity(set, var->data.aggregate.var->locdom.ub))
8058 || SCIPsetIsFeasEQ(set, var->locdom.lb,
8059 var->data.aggregate.var->locdom.ub * var->data.aggregate.scalar + var->data.aggregate.constant));
8060 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
8061 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
8062 else
8063 childnewbound = -newbound;
8064 SCIP_CALL( SCIPvarChgUbLocal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue,
8065 childnewbound) );
8066 }
8067 else
8068 {
8069 SCIPerrorMessage("scalar is zero in aggregation\n");
8070 return SCIP_INVALIDDATA;
8071 }
8072 break;
8073
8074 case SCIP_VARSTATUS_MULTAGGR:
8075 SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
8076 return SCIP_INVALIDDATA;
8077
8078 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
8079 assert(var->negatedvar != NULL);
8080 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
8081 assert(var->negatedvar->negatedvar == var);
8082 SCIP_CALL( SCIPvarChgUbLocal(var->negatedvar, blkmem, set, stat, lp, branchcand, eventqueue,
8083 var->data.negate.constant - newbound) );
8084 break;
8085
8086 default:
8087 SCIPerrorMessage("unknown variable status\n");
8088 return SCIP_INVALIDDATA;
8089 }
8090
8091 return SCIP_OKAY;
8092 }
8093
8094 /** changes current local upper bound of variable; if possible, adjusts bound to integral value; stores inference
8095 * information in variable
8096 */
8097 SCIP_RETCODE SCIPvarChgUbLocal(
8098 SCIP_VAR* var, /**< problem variable to change */
8099 BMS_BLKMEM* blkmem, /**< block memory */
8100 SCIP_SET* set, /**< global SCIP settings */
8101 SCIP_STAT* stat, /**< problem statistics */
8102 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
8103 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
8104 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
8105 SCIP_Real newbound /**< new bound for variable */
8106 )
8107 {
8108 assert(var != NULL);
8109 assert(blkmem != NULL);
8110 assert(set != NULL);
8111 assert(var->scip == set->scip);
8112
8113 /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
8114 * of the domain within feastol
8115 */
8116 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasLT(set, newbound, var->locdom.lb));
8117
8118 /* adjust bound to integral value if variable is of integral type */
8119 newbound = adjustedUb(set, SCIPvarGetType(var), newbound);
8120
8121 /* check that the adjusted bound is feasible */
8122 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasLT(set, newbound, var->locdom.lb));
8123
8124 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
8125 {
8126 /* we do not want to undercut the lowerbound, which could have happened due to numerics */
8127 newbound = MAX(newbound, var->locdom.lb);
8128 }
8129 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
8130
8131 SCIPsetDebugMsg(set, "changing upper bound of <%s>[%g,%g] to %g\n", var->name, var->locdom.lb, var->locdom.ub, newbound);
8132
8133 if( SCIPsetIsEQ(set, var->locdom.ub, newbound) && (!SCIPsetIsEQ(set, var->glbdom.ub, newbound) || var->locdom.ub == newbound) /*lint !e777*/
8134 && !(newbound != var->locdom.ub && newbound * var->locdom.ub <= 0.0) ) /*lint !e777*/
8135 return SCIP_OKAY;
8136
8137 /* change bounds of attached variables */
8138 switch( SCIPvarGetStatus(var) )
8139 {
8140 case SCIP_VARSTATUS_ORIGINAL:
8141 if( var->data.original.transvar != NULL )
8142 {
8143 SCIP_CALL( SCIPvarChgUbLocal(var->data.original.transvar, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
8144 }
8145 else
8146 {
8147 assert(set->stage == SCIP_STAGE_PROBLEM);
8148 SCIP_CALL( varProcessChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
8149 }
8150 break;
8151
8152 case SCIP_VARSTATUS_COLUMN:
8153 case SCIP_VARSTATUS_LOOSE:
8154 SCIP_CALL( varProcessChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
8155 break;
8156
8157 case SCIP_VARSTATUS_FIXED:
8158 SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
8159 return SCIP_INVALIDDATA;
8160
8161 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
8162 assert(var->data.aggregate.var != NULL);
8163 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
8164 {
8165 SCIP_Real childnewbound;
8166
8167 /* a > 0 -> change upper bound of y */
8168 assert((SCIPsetIsInfinity(set, var->locdom.ub) && SCIPsetIsInfinity(set, var->data.aggregate.var->locdom.ub))
8169 || SCIPsetIsFeasEQ(set, var->locdom.ub,
8170 var->data.aggregate.var->locdom.ub * var->data.aggregate.scalar + var->data.aggregate.constant));
8171 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
8172 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
8173 else
8174 childnewbound = newbound;
8175 SCIP_CALL( SCIPvarChgUbLocal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue,
8176 childnewbound) );
8177 }
8178 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
8179 {
8180 SCIP_Real childnewbound;
8181
8182 /* a < 0 -> change lower bound of y */
8183 assert((SCIPsetIsInfinity(set, var->locdom.ub) && SCIPsetIsInfinity(set, -var->data.aggregate.var->locdom.lb))
8184 || SCIPsetIsFeasEQ(set, var->locdom.ub,
8185 var->data.aggregate.var->locdom.lb * var->data.aggregate.scalar + var->data.aggregate.constant));
8186 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
8187 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
8188 else
8189 childnewbound = -newbound;
8190 SCIP_CALL( SCIPvarChgLbLocal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue,
8191 childnewbound) );
8192 }
8193 else
8194 {
8195 SCIPerrorMessage("scalar is zero in aggregation\n");
8196 return SCIP_INVALIDDATA;
8197 }
8198 break;
8199
8200 case SCIP_VARSTATUS_MULTAGGR:
8201 SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
8202 return SCIP_INVALIDDATA;
8203
8204 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
8205 assert(var->negatedvar != NULL);
8206 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
8207 assert(var->negatedvar->negatedvar == var);
8208 SCIP_CALL( SCIPvarChgLbLocal(var->negatedvar, blkmem, set, stat, lp, branchcand, eventqueue,
8209 var->data.negate.constant - newbound) );
8210 break;
8211
8212 default:
8213 SCIPerrorMessage("unknown variable status\n");
8214 return SCIP_INVALIDDATA;
8215 }
8216
8217 return SCIP_OKAY;
8218 }
8219
8220 /** changes current local bound of variable; if possible, adjusts bound to integral value; stores inference
8221 * information in variable
8222 */
8223 SCIP_RETCODE SCIPvarChgBdLocal(
8224 SCIP_VAR* var, /**< problem variable to change */
8225 BMS_BLKMEM* blkmem, /**< block memory */
8226 SCIP_SET* set, /**< global SCIP settings */
8227 SCIP_STAT* stat, /**< problem statistics */
8228 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
8229 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
8230 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
8231 SCIP_Real newbound, /**< new bound for variable */
8232 SCIP_BOUNDTYPE boundtype /**< type of bound: lower or upper bound */
8233 )
8234 {
8235 /* apply bound change to the LP data */
8236 switch( boundtype )
8237 {
8238 case SCIP_BOUNDTYPE_LOWER:
8239 return SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound);
8240 case SCIP_BOUNDTYPE_UPPER:
8241 return SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound);
8242 default:
8243 SCIPerrorMessage("unknown bound type\n");
8244 return SCIP_INVALIDDATA;
8245 }
8246 }
8247
8248 /** changes lower bound of variable in current dive; if possible, adjusts bound to integral value */
8249 SCIP_RETCODE SCIPvarChgLbDive(
8250 SCIP_VAR* var, /**< problem variable to change */
8251 SCIP_SET* set, /**< global SCIP settings */
8252 SCIP_LP* lp, /**< current LP data */
8253 SCIP_Real newbound /**< new bound for variable */
8254 )
8255 {
8256 assert(var != NULL);
8257 assert(set != NULL);
8258 assert(var->scip == set->scip);
8259 assert(lp != NULL);
8260 assert(SCIPlpDiving(lp));
8261
8262 /* adjust bound for integral variables */
8263 SCIPvarAdjustLb(var, set, &newbound);
8264
8265 SCIPsetDebugMsg(set, "changing lower bound of <%s> to %g in current dive\n", var->name, newbound);
8266
8267 /* change bounds of attached variables */
8268 switch( SCIPvarGetStatus(var) )
8269 {
8270 case SCIP_VARSTATUS_ORIGINAL:
8271 assert(var->data.original.transvar != NULL);
8272 SCIP_CALL( SCIPvarChgLbDive(var->data.original.transvar, set, lp, newbound) );
8273 break;
8274
8275 case SCIP_VARSTATUS_COLUMN:
8276 assert(var->data.col != NULL);
8277 SCIP_CALL( SCIPcolChgLb(var->data.col, set, lp, newbound) );
8278 break;
8279
8280 case SCIP_VARSTATUS_LOOSE:
8281 SCIPerrorMessage("cannot change variable's bounds in dive for LOOSE variables\n");
8282 return SCIP_INVALIDDATA;
8283
8284 case SCIP_VARSTATUS_FIXED:
8285 SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
8286 return SCIP_INVALIDDATA;
8287
8288 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
8289 assert(var->data.aggregate.var != NULL);
8290 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
8291 {
8292 SCIP_Real childnewbound;
8293
8294 /* a > 0 -> change lower bound of y */
8295 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
8296 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
8297 else
8298 childnewbound = newbound;
8299 SCIP_CALL( SCIPvarChgLbDive(var->data.aggregate.var, set, lp, childnewbound) );
8300 }
8301 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
8302 {
8303 SCIP_Real childnewbound;
8304
8305 /* a < 0 -> change upper bound of y */
8306 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
8307 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
8308 else
8309 childnewbound = -newbound;
8310 SCIP_CALL( SCIPvarChgUbDive(var->data.aggregate.var, set, lp, childnewbound) );
8311 }
8312 else
8313 {
8314 SCIPerrorMessage("scalar is zero in aggregation\n");
8315 return SCIP_INVALIDDATA;
8316 }
8317 break;
8318
8319 case SCIP_VARSTATUS_MULTAGGR:
8320 SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
8321 return SCIP_INVALIDDATA;
8322
8323 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
8324 assert(var->negatedvar != NULL);
8325 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
8326 assert(var->negatedvar->negatedvar == var);
8327 SCIP_CALL( SCIPvarChgUbDive(var->negatedvar, set, lp, var->data.negate.constant - newbound) );
8328 break;
8329
8330 default:
8331 SCIPerrorMessage("unknown variable status\n");
8332 return SCIP_INVALIDDATA;
8333 }
8334
8335 return SCIP_OKAY;
8336 }
8337
8338 /** changes upper bound of variable in current dive; if possible, adjusts bound to integral value */
8339 SCIP_RETCODE SCIPvarChgUbDive(
8340 SCIP_VAR* var, /**< problem variable to change */
8341 SCIP_SET* set, /**< global SCIP settings */
8342 SCIP_LP* lp, /**< current LP data */
8343 SCIP_Real newbound /**< new bound for variable */
8344 )
8345 {
8346 assert(var != NULL);
8347 assert(set != NULL);
8348 assert(var->scip == set->scip);
8349 assert(lp != NULL);
8350 assert(SCIPlpDiving(lp));
8351
8352 /* adjust bound for integral variables */
8353 SCIPvarAdjustUb(var, set, &newbound);
8354
8355 SCIPsetDebugMsg(set, "changing upper bound of <%s> to %g in current dive\n", var->name, newbound);
8356
8357 /* change bounds of attached variables */
8358 switch( SCIPvarGetStatus(var) )
8359 {
8360 case SCIP_VARSTATUS_ORIGINAL:
8361 assert(var->data.original.transvar != NULL);
8362 SCIP_CALL( SCIPvarChgUbDive(var->data.original.transvar, set, lp, newbound) );
8363 break;
8364
8365 case SCIP_VARSTATUS_COLUMN:
8366 assert(var->data.col != NULL);
8367 SCIP_CALL( SCIPcolChgUb(var->data.col, set, lp, newbound) );
8368 break;
8369
8370 case SCIP_VARSTATUS_LOOSE:
8371 SCIPerrorMessage("cannot change variable's bounds in dive for LOOSE variables\n");
8372 return SCIP_INVALIDDATA;
8373
8374 case SCIP_VARSTATUS_FIXED:
8375 SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
8376 return SCIP_INVALIDDATA;
8377
8378 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
8379 assert(var->data.aggregate.var != NULL);
8380 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
8381 {
8382 SCIP_Real childnewbound;
8383
8384 /* a > 0 -> change upper bound of y */
8385 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
8386 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
8387 else
8388 childnewbound = newbound;
8389 SCIP_CALL( SCIPvarChgUbDive(var->data.aggregate.var, set, lp, childnewbound) );
8390 }
8391 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
8392 {
8393 SCIP_Real childnewbound;
8394
8395 /* a < 0 -> change lower bound of y */
8396 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
8397 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
8398 else
8399 childnewbound = -newbound;
8400 SCIP_CALL( SCIPvarChgLbDive(var->data.aggregate.var, set, lp, childnewbound) );
8401 }
8402 else
8403 {
8404 SCIPerrorMessage("scalar is zero in aggregation\n");
8405 return SCIP_INVALIDDATA;
8406 }
8407 break;
8408
8409 case SCIP_VARSTATUS_MULTAGGR:
8410 SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
8411 return SCIP_INVALIDDATA;
8412
8413 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
8414 assert(var->negatedvar != NULL);
8415 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
8416 assert(var->negatedvar->negatedvar == var);
8417 SCIP_CALL( SCIPvarChgLbDive(var->negatedvar, set, lp, var->data.negate.constant - newbound) );
8418 break;
8419
8420 default:
8421 SCIPerrorMessage("unknown variable status\n");
8422 return SCIP_INVALIDDATA;
8423 }
8424
8425 return SCIP_OKAY;
8426 }
8427
8428 /** for a multi-aggregated variable, gives the local lower bound computed by adding the local bounds from all
8429 * aggregation variables, this lower bound may be tighter than the one given by SCIPvarGetLbLocal, since the latter is
8430 * not updated if bounds of aggregation variables are changing
8431 *
8432 * calling this function for a non-multi-aggregated variable is not allowed
8433 */
8434 SCIP_Real SCIPvarGetMultaggrLbLocal(
8435 SCIP_VAR* var, /**< problem variable */
8436 SCIP_SET* set /**< global SCIP settings */
8437 )
8438 {
8439 int i;
8440 SCIP_Real lb;
8441 SCIP_Real bnd;
8442 SCIP_VAR* aggrvar;
8443 SCIP_Bool posinf;
8444 SCIP_Bool neginf;
8445
8446 assert(var != NULL);
8447 assert(set != NULL);
8448 assert(var->scip == set->scip);
8449 assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);
8450
8451 posinf = FALSE;
8452 neginf = FALSE;
8453 lb = var->data.multaggr.constant;
8454 for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
8455 {
8456 aggrvar = var->data.multaggr.vars[i];
8457 if( var->data.multaggr.scalars[i] > 0.0 )
8458 {
8459 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrLbLocal(aggrvar, set) : SCIPvarGetLbLocal(aggrvar);
8460
8461 if( SCIPsetIsInfinity(set, bnd) )
8462 posinf = TRUE;
8463 else if( SCIPsetIsInfinity(set, -bnd) )
8464 neginf = TRUE;
8465 else
8466 lb += var->data.multaggr.scalars[i] * bnd;
8467 }
8468 else
8469 {
8470 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrUbLocal(aggrvar, set) : SCIPvarGetUbLocal(aggrvar);
8471
8472 if( SCIPsetIsInfinity(set, -bnd) )
8473 posinf = TRUE;
8474 else if( SCIPsetIsInfinity(set, bnd) )
8475 neginf = TRUE;
8476 else
8477 lb += var->data.multaggr.scalars[i] * bnd;
8478 }
8479
8480 /* stop if two diffrent infinities (or a -infinity) were found and return local lower bound of multi aggregated
8481 * variable
8482 */
8483 if( neginf )
8484 return SCIPvarGetLbLocal(var);
8485 }
8486
8487 /* if positive infinity flag was set to true return infinity */
8488 if( posinf )
8489 return SCIPsetInfinity(set);
8490
8491 return (MAX(lb, SCIPvarGetLbLocal(var))); /*lint !e666*/
8492 }
8493
8494 /** for a multi-aggregated variable, gives the local upper bound computed by adding the local bounds from all
8495 * aggregation variables, this upper bound may be tighter than the one given by SCIPvarGetUbLocal, since the latter is
8496 * not updated if bounds of aggregation variables are changing
8497 *
8498 * calling this function for a non-multi-aggregated variable is not allowed
8499 */
8500 SCIP_Real SCIPvarGetMultaggrUbLocal(
8501 SCIP_VAR* var, /**< problem variable */
8502 SCIP_SET* set /**< global SCIP settings */
8503 )
8504 {
8505 int i;
8506 SCIP_Real ub;
8507 SCIP_Real bnd;
8508 SCIP_VAR* aggrvar;
8509 SCIP_Bool posinf;
8510 SCIP_Bool neginf;
8511
8512 assert(var != NULL);
8513 assert(set != NULL);
8514 assert(var->scip == set->scip);
8515 assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);
8516
8517 posinf = FALSE;
8518 neginf = FALSE;
8519 ub = var->data.multaggr.constant;
8520 for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
8521 {
8522 aggrvar = var->data.multaggr.vars[i];
8523 if( var->data.multaggr.scalars[i] > 0.0 )
8524 {
8525 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrUbLocal(aggrvar, set) : SCIPvarGetUbLocal(aggrvar);
8526
8527 if( SCIPsetIsInfinity(set, bnd) )
8528 posinf = TRUE;
8529 else if( SCIPsetIsInfinity(set, -bnd) )
8530 neginf = TRUE;
8531 else
8532 ub += var->data.multaggr.scalars[i] * bnd;
8533 }
8534 else
8535 {
8536 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrLbLocal(aggrvar, set) : SCIPvarGetLbLocal(aggrvar);
8537
8538 if( SCIPsetIsInfinity(set, -bnd) )
8539 posinf = TRUE;
8540 else if( SCIPsetIsInfinity(set, bnd) )
8541 neginf = TRUE;
8542 else
8543 ub += var->data.multaggr.scalars[i] * bnd;
8544 }
8545
8546 /* stop if two diffrent infinities (or a -infinity) were found and return local upper bound of multi aggregated
8547 * variable
8548 */
8549 if( posinf )
8550 return SCIPvarGetUbLocal(var);
8551 }
8552
8553 /* if negative infinity flag was set to true return -infinity */
8554 if( neginf )
8555 return -SCIPsetInfinity(set);
8556
8557 return (MIN(ub, SCIPvarGetUbLocal(var))); /*lint !e666*/
8558 }
8559
8560 /** for a multi-aggregated variable, gives the global lower bound computed by adding the global bounds from all
8561 * aggregation variables, this global bound may be tighter than the one given by SCIPvarGetLbGlobal, since the latter is
8562 * not updated if bounds of aggregation variables are changing
8563 *
8564 * calling this function for a non-multi-aggregated variable is not allowed
8565 */
8566 SCIP_Real SCIPvarGetMultaggrLbGlobal(
8567 SCIP_VAR* var, /**< problem variable */
8568 SCIP_SET* set /**< global SCIP settings */
8569 )
8570 {
8571 int i;
8572 SCIP_Real lb;
8573 SCIP_Real bnd;
8574 SCIP_VAR* aggrvar;
8575 SCIP_Bool posinf;
8576 SCIP_Bool neginf;
8577
8578 assert(var != NULL);
8579 assert(set != NULL);
8580 assert(var->scip == set->scip);
8581 assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);
8582
8583 posinf = FALSE;
8584 neginf = FALSE;
8585 lb = var->data.multaggr.constant;
8586 for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
8587 {
8588 aggrvar = var->data.multaggr.vars[i];
8589 if( var->data.multaggr.scalars[i] > 0.0 )
8590 {
8591 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrLbGlobal(aggrvar, set) : SCIPvarGetLbGlobal(aggrvar);
8592
8593 if( SCIPsetIsInfinity(set, bnd) )
8594 posinf = TRUE;
8595 else if( SCIPsetIsInfinity(set, -bnd) )
8596 neginf = TRUE;
8597 else
8598 lb += var->data.multaggr.scalars[i] * bnd;
8599 }
8600 else
8601 {
8602 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrUbGlobal(aggrvar, set) : SCIPvarGetUbGlobal(aggrvar);
8603
8604 if( SCIPsetIsInfinity(set, -bnd) )
8605 posinf = TRUE;
8606 else if( SCIPsetIsInfinity(set, bnd) )
8607 neginf = TRUE;
8608 else
8609 lb += var->data.multaggr.scalars[i] * bnd;
8610 }
8611
8612 /* stop if two diffrent infinities (or a -infinity) were found and return global lower bound of multi aggregated
8613 * variable
8614 */
8615 if( neginf )
8616 return SCIPvarGetLbGlobal(var);
8617 }
8618
8619 /* if positive infinity flag was set to true return infinity */
8620 if( posinf )
8621 return SCIPsetInfinity(set);
8622
8623 return (MAX(lb, SCIPvarGetLbGlobal(var))); /*lint !e666*/
8624 }
8625
8626 /** for a multi-aggregated variable, gives the global upper bound computed by adding the global bounds from all
8627 * aggregation variables, this upper bound may be tighter than the one given by SCIPvarGetUbGlobal, since the latter is
8628 * not updated if bounds of aggregation variables are changing
8629 *
8630 * calling this function for a non-multi-aggregated variable is not allowed
8631 */
8632 SCIP_Real SCIPvarGetMultaggrUbGlobal(
8633 SCIP_VAR* var, /**< problem variable */
8634 SCIP_SET* set /**< global SCIP settings */
8635 )
8636 {
8637 int i;
8638 SCIP_Real ub;
8639 SCIP_Real bnd;
8640 SCIP_VAR* aggrvar;
8641 SCIP_Bool posinf;
8642 SCIP_Bool neginf;
8643
8644 assert(var != NULL);
8645 assert(set != NULL);
8646 assert(var->scip == set->scip);
8647 assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);
8648
8649 posinf = FALSE;
8650 neginf = FALSE;
8651 ub = var->data.multaggr.constant;
8652 for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
8653 {
8654 aggrvar = var->data.multaggr.vars[i];
8655 if( var->data.multaggr.scalars[i] > 0.0 )
8656 {
8657 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrUbGlobal(aggrvar, set) : SCIPvarGetUbGlobal(aggrvar);
8658
8659 if( SCIPsetIsInfinity(set, bnd) )
8660 posinf = TRUE;
8661 else if( SCIPsetIsInfinity(set, -bnd) )
8662 neginf = TRUE;
8663 else
8664 ub += var->data.multaggr.scalars[i] * bnd;
8665 }
8666 else
8667 {
8668 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrLbGlobal(aggrvar, set) : SCIPvarGetLbGlobal(aggrvar);
8669
8670 if( SCIPsetIsInfinity(set, -bnd) )
8671 posinf = TRUE;
8672 else if( SCIPsetIsInfinity(set, bnd) )
8673 neginf = TRUE;
8674 else
8675 ub += var->data.multaggr.scalars[i] * bnd;
8676 }
8677
8678 /* stop if two diffrent infinities (or a -infinity) were found and return local upper bound of multi aggregated
8679 * variable
8680 */
8681 if( posinf )
8682 return SCIPvarGetUbGlobal(var);
8683 }
8684
8685 /* if negative infinity flag was set to true return -infinity */
8686 if( neginf )
8687 return -SCIPsetInfinity(set);
8688
8689 return (MIN(ub, SCIPvarGetUbGlobal(var))); /*lint !e666*/
8690 }
8691
8692 /** adds a hole to the original domain of the variable */
8693 SCIP_RETCODE SCIPvarAddHoleOriginal(
8694 SCIP_VAR* var, /**< problem variable */
8695 BMS_BLKMEM* blkmem, /**< block memory */
8696 SCIP_SET* set, /**< global SCIP settings */
8697 SCIP_Real left, /**< left bound of open interval in new hole */
8698 SCIP_Real right /**< right bound of open interval in new hole */
8699 )
8700 {
8701 SCIP_Bool added;
8702
8703 assert(var != NULL);
8704 assert(!SCIPvarIsTransformed(var));
8705 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
8706 assert(SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS);
8707 assert(set != NULL);
8708 assert(var->scip == set->scip);
8709 assert(set->stage == SCIP_STAGE_PROBLEM);
8710
8711 SCIPsetDebugMsg(set, "adding original hole (%g,%g) to <%s>\n", left, right, var->name);
8712
8713 if( SCIPsetIsEQ(set, left, right) )
8714 return SCIP_OKAY;
8715
8716 /* the interval should not be empty */
8717 assert(SCIPsetIsLT(set, left, right));
8718
8719 /* the the interval bound should already be adjusted */
8720 assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarGetType(var), left)));
8721 assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarGetType(var), right)));
8722
8723 /* the the interval should lay between the lower and upper bound */
8724 assert(SCIPsetIsGE(set, left, SCIPvarGetLbOriginal(var)));
8725 assert(SCIPsetIsLE(set, right, SCIPvarGetUbOriginal(var)));
8726
8727 /* add domain hole */
8728 SCIP_CALL( domAddHole(&var->data.original.origdom, blkmem, set, left, right, &added) );
8729
8730 /* merges overlapping holes into single holes, moves bounds respectively if hole was added */
8731 if( added )
8732 {
8733 domMerge(&var->data.original.origdom, blkmem, set, NULL, NULL);
8734 }
8735
8736 /**@todo add hole in parent and child variables (just like with bound changes);
8737 * warning! original vars' holes are in original blkmem, transformed vars' holes in transformed blkmem
8738 */
8739
8740 return SCIP_OKAY;
8741 }
8742
8743 /** performs the current add of domain, changes all parents accordingly */
8744 static
8745 SCIP_RETCODE varProcessAddHoleGlobal(
8746 SCIP_VAR* var, /**< problem variable */
8747 BMS_BLKMEM* blkmem, /**< block memory */
8748 SCIP_SET* set, /**< global SCIP settings */
8749 SCIP_STAT* stat, /**< problem statistics */
8750 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
8751 SCIP_Real left, /**< left bound of open interval in new hole */
8752 SCIP_Real right, /**< right bound of open interval in new hole */
8753 SCIP_Bool* added /**< pointer to store whether the hole was added */
8754 )
8755 {
8756 SCIP_VAR* parentvar;
8757 SCIP_Real newlb;
8758 SCIP_Real newub;
8759 int i;
8760
8761 assert(var != NULL);
8762 assert(added != NULL);
8763 assert(blkmem != NULL);
8764
8765 /* the interval should not be empty */
8766 assert(SCIPsetIsLT(set, left, right));
8767
8768 /* the interval bound should already be adjusted */
8769 assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarGetType(var), left)));
8770 assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarGetType(var), right)));
8771
8772 /* the interval should lay between the lower and upper bound */
8773 assert(SCIPsetIsGE(set, left, SCIPvarGetLbGlobal(var)));
8774 assert(SCIPsetIsLE(set, right, SCIPvarGetUbGlobal(var)));
8775
8776 /* @todo add debugging mechanism for holes when using a debugging solution */
8777
8778 /* add hole to hole list */
8779 SCIP_CALL( domAddHole(&var->glbdom, blkmem, set, left, right, added) );
8780
8781 /* check if the hole is redundant */
8782 if( !(*added) )
8783 return SCIP_OKAY;
8784
8785 /* current bounds */
8786 newlb = var->glbdom.lb;
8787 newub = var->glbdom.ub;
8788
8789 /* merge domain holes */
8790 domMerge(&var->glbdom, blkmem, set, &newlb, &newub);
8791
8792 /* the bound should not be changed */
8793 assert(SCIPsetIsEQ(set, newlb, var->glbdom.lb));
8794 assert(SCIPsetIsEQ(set, newub, var->glbdom.ub));
8795
8796 /* issue bound change event */
8797 assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
8798 if( var->eventfilter != NULL )
8799 {
8800 SCIP_CALL( varEventGholeAdded(var, blkmem, set, eventqueue, left, right) );
8801 }
8802
8803 /* process parent variables */
8804 for( i = 0; i < var->nparentvars; ++i )
8805 {
8806 SCIP_Real parentnewleft;
8807 SCIP_Real parentnewright;
8808 SCIP_Bool localadded;
8809
8810 parentvar = var->parentvars[i];
8811 assert(parentvar != NULL);
8812
8813 switch( SCIPvarGetStatus(parentvar) )
8814 {
8815 case SCIP_VARSTATUS_ORIGINAL:
8816 parentnewleft = left;
8817 parentnewright = right;
8818 break;
8819
8820 case SCIP_VARSTATUS_COLUMN:
8821 case SCIP_VARSTATUS_LOOSE:
8822 case SCIP_VARSTATUS_FIXED:
8823 case SCIP_VARSTATUS_MULTAGGR:
8824 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
8825 return SCIP_INVALIDDATA;
8826
8827 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
8828 assert(parentvar->data.aggregate.var == var);
8829
8830 if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
8831 {
8832 /* a > 0 -> change upper bound of x */
8833 parentnewleft = parentvar->data.aggregate.scalar * left + parentvar->data.aggregate.constant;
8834 parentnewright = parentvar->data.aggregate.scalar * right + parentvar->data.aggregate.constant;
8835 }
8836 else
8837 {
8838 /* a < 0 -> change lower bound of x */
8839 assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
8840
8841 parentnewright = parentvar->data.aggregate.scalar * left + parentvar->data.aggregate.constant;
8842 parentnewleft = parentvar->data.aggregate.scalar * right + parentvar->data.aggregate.constant;
8843 }
8844 break;
8845
8846 case SCIP_VARSTATUS_NEGATED: /* x = offset - x' -> x' = offset - x */
8847 assert(parentvar->negatedvar != NULL);
8848 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
8849 assert(parentvar->negatedvar->negatedvar == parentvar);
8850
8851 parentnewright = -left + parentvar->data.negate.constant;
8852 parentnewleft = -right + parentvar->data.negate.constant;
8853 break;
8854
8855 default:
8856 SCIPerrorMessage("unknown variable status\n");
8857 return SCIP_INVALIDDATA;
8858 }
8859
8860 SCIPsetDebugMsg(set, "add global hole (%g,%g) to parent variable <%s>\n", parentnewleft, parentnewright, SCIPvarGetName(parentvar));
8861
8862 /* perform hole added for parent variable */
8863 assert(blkmem != NULL);
8864 assert(SCIPsetIsLT(set, parentnewleft, parentnewright));
8865 SCIP_CALL( varProcessAddHoleGlobal(parentvar, blkmem, set, stat, eventqueue,
8866 parentnewleft, parentnewright, &localadded) );
8867 assert(localadded);
8868 }
8869
8870 return SCIP_OKAY;
8871 }
8872
8873 /** adds a hole to the variable's global and local domain */
8874 SCIP_RETCODE SCIPvarAddHoleGlobal(
8875 SCIP_VAR* var, /**< problem variable */
8876 BMS_BLKMEM* blkmem, /**< block memory */
8877 SCIP_SET* set, /**< global SCIP settings */
8878 SCIP_STAT* stat, /**< problem statistics */
8879 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
8880 SCIP_Real left, /**< left bound of open interval in new hole */
8881 SCIP_Real right, /**< right bound of open interval in new hole */
8882 SCIP_Bool* added /**< pointer to store whether the hole was added */
8883 )
8884 {
8885 SCIP_Real childnewleft;
8886 SCIP_Real childnewright;
8887
8888 assert(var != NULL);
8889 assert(SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS);
8890 assert(blkmem != NULL);
8891 assert(added != NULL);
8892
8893 SCIPsetDebugMsg(set, "adding global hole (%g,%g) to <%s>\n", left, right, var->name);
8894
8895 /* the interval should not be empty */
8896 assert(SCIPsetIsLT(set, left, right));
8897
8898 /* the the interval bound should already be adjusted */
8899 assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarGetType(var), left)));
8900 assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarGetType(var), right)));
8901
8902 /* the the interval should lay between the lower and upper bound */
8903 assert(SCIPsetIsGE(set, left, SCIPvarGetLbGlobal(var)));
8904 assert(SCIPsetIsLE(set, right, SCIPvarGetUbGlobal(var)));
8905
8906 /* change bounds of attached variables */
8907 switch( SCIPvarGetStatus(var) )
8908 {
8909 case SCIP_VARSTATUS_ORIGINAL:
8910 if( var->data.original.transvar != NULL )
8911 {
8912 SCIP_CALL( SCIPvarAddHoleGlobal(var->data.original.transvar, blkmem, set, stat, eventqueue,
8913 left, right, added) );
8914 }
8915 else
8916 {
8917 assert(set->stage == SCIP_STAGE_PROBLEM);
8918
8919 SCIP_CALL( varProcessAddHoleGlobal(var, blkmem, set, stat, eventqueue, left, right, added) );
8920 if( *added )
8921 {
8922 SCIP_Bool localadded;
8923
8924 SCIP_CALL( SCIPvarAddHoleLocal(var, blkmem, set, stat, eventqueue, left, right, &localadded) );
8925 }
8926 }
8927 break;
8928
8929 case SCIP_VARSTATUS_COLUMN:
8930 case SCIP_VARSTATUS_LOOSE:
8931 SCIP_CALL( varProcessAddHoleGlobal(var, blkmem, set, stat, eventqueue, left, right, added) );
8932 if( *added )
8933 {
8934 SCIP_Bool localadded;
8935
8936 SCIP_CALL( SCIPvarAddHoleLocal(var, blkmem, set, stat, eventqueue, left, right, &localadded) );
8937 }
8938 break;
8939
8940 case SCIP_VARSTATUS_FIXED:
8941 SCIPerrorMessage("cannot add hole of a fixed variable\n");
8942 return SCIP_INVALIDDATA;
8943
8944 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
8945 assert(var->data.aggregate.var != NULL);
8946
8947 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
8948 {
8949 /* a > 0 -> change lower bound of y */
8950 childnewleft = (left - var->data.aggregate.constant)/var->data.aggregate.scalar;
8951 childnewright = (right - var->data.aggregate.constant)/var->data.aggregate.scalar;
8952 }
8953 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
8954 {
8955 childnewright = (left - var->data.aggregate.constant)/var->data.aggregate.scalar;
8956 childnewleft = (right - var->data.aggregate.constant)/var->data.aggregate.scalar;
8957 }
8958 else
8959 {
8960 SCIPerrorMessage("scalar is zero in aggregation\n");
8961 return SCIP_INVALIDDATA;
8962 }
8963 SCIP_CALL( SCIPvarAddHoleGlobal(var->data.aggregate.var, blkmem, set, stat, eventqueue,
8964 childnewleft, childnewright, added) );
8965 break;
8966
8967 case SCIP_VARSTATUS_MULTAGGR:
8968 SCIPerrorMessage("cannot add a hole of a multi-aggregated variable.\n");
8969 return SCIP_INVALIDDATA;
8970
8971 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
8972 assert(var->negatedvar != NULL);
8973 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
8974 assert(var->negatedvar->negatedvar == var);
8975
8976 childnewright = -left + var->data.negate.constant;
8977 childnewleft = -right + var->data.negate.constant;
8978
8979 SCIP_CALL( SCIPvarAddHoleGlobal(var->negatedvar, blkmem, set, stat, eventqueue,
8980 childnewleft, childnewright, added) );
8981 break;
8982
8983 default:
8984 SCIPerrorMessage("unknown variable status\n");
8985 return SCIP_INVALIDDATA;
8986 }
8987
8988 return SCIP_OKAY;
8989 }
8990
8991 /** performs the current add of domain, changes all parents accordingly */
8992 static
8993 SCIP_RETCODE varProcessAddHoleLocal(
8994 SCIP_VAR* var, /**< problem variable */
8995 BMS_BLKMEM* blkmem, /**< block memory */
8996 SCIP_SET* set, /**< global SCIP settings */
8997 SCIP_STAT* stat, /**< problem statistics */
8998 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
8999 SCIP_Real left, /**< left bound of open interval in new hole */
9000 SCIP_Real right, /**< right bound of open interval in new hole */
9001 SCIP_Bool* added /**< pointer to store whether the hole was added, or NULL */
9002 )
9003 {
9004 SCIP_VAR* parentvar;
9005 SCIP_Real newlb;
9006 SCIP_Real newub;
9007 int i;
9008
9009 assert(var != NULL);
9010 assert(added != NULL);
9011 assert(blkmem != NULL);
9012
9013 /* the interval should not be empty */
9014 assert(SCIPsetIsLT(set, left, right));
9015
9016 /* the the interval bound should already be adjusted */
9017 assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarGetType(var), left)));
9018 assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarGetType(var), right)));
9019
9020 /* the the interval should lay between the lower and upper bound */
9021 assert(SCIPsetIsGE(set, left, SCIPvarGetLbLocal(var)));
9022 assert(SCIPsetIsLE(set, right, SCIPvarGetUbLocal(var)));
9023
9024 /* add hole to hole list */
9025 SCIP_CALL( domAddHole(&var->locdom, blkmem, set, left, right, added) );
9026
9027 /* check if the hole is redundant */
9028 if( !(*added) )
9029 return SCIP_OKAY;
9030
9031 /* current bounds */
9032 newlb = var->locdom.lb;
9033 newub = var->locdom.ub;
9034
9035 /* merge domain holes */
9036 domMerge(&var->locdom, blkmem, set, &newlb, &newub);
9037
9038 /* the bound should not be changed */
9039 assert(SCIPsetIsEQ(set, newlb, var->locdom.lb));
9040 assert(SCIPsetIsEQ(set, newub, var->locdom.ub));
9041
9042 #if 0
9043 /* issue bound change event */
9044 assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
9045 if( var->eventfilter != NULL )
9046 {
9047 SCIP_CALL( varEventLholeAdded(var, blkmem, set, lp, branchcand, eventqueue, left, right) );
9048 }
9049 #endif
9050
9051 /* process parent variables */
9052 for( i = 0; i < var->nparentvars; ++i )
9053 {
9054 SCIP_Real parentnewleft;
9055 SCIP_Real parentnewright;
9056 SCIP_Bool localadded;
9057
9058 parentvar = var->parentvars[i];
9059 assert(parentvar != NULL);
9060
9061 switch( SCIPvarGetStatus(parentvar) )
9062 {
9063 case SCIP_VARSTATUS_ORIGINAL:
9064 parentnewleft = left;
9065 parentnewright = right;
9066 break;
9067
9068 case SCIP_VARSTATUS_COLUMN:
9069 case SCIP_VARSTATUS_LOOSE:
9070 case SCIP_VARSTATUS_FIXED:
9071 case SCIP_VARSTATUS_MULTAGGR:
9072 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
9073 return SCIP_INVALIDDATA;
9074
9075 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
9076 assert(parentvar->data.aggregate.var == var);
9077
9078 if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
9079 {
9080 /* a > 0 -> change upper bound of x */
9081 parentnewleft = parentvar->data.aggregate.scalar * left + parentvar->data.aggregate.constant;
9082 parentnewright = parentvar->data.aggregate.scalar * right + parentvar->data.aggregate.constant;
9083 }
9084 else
9085 {
9086 /* a < 0 -> change lower bound of x */
9087 assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
9088
9089 parentnewright = parentvar->data.aggregate.scalar * left + parentvar->data.aggregate.constant;
9090 parentnewleft = parentvar->data.aggregate.scalar * right + parentvar->data.aggregate.constant;
9091 }
9092 break;
9093
9094 case SCIP_VARSTATUS_NEGATED: /* x = offset - x' -> x' = offset - x */
9095 assert(parentvar->negatedvar != NULL);
9096 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
9097 assert(parentvar->negatedvar->negatedvar == parentvar);
9098
9099 parentnewright = -left + parentvar->data.negate.constant;
9100 parentnewleft = -right + parentvar->data.negate.constant;
9101 break;
9102
9103 default:
9104 SCIPerrorMessage("unknown variable status\n");
9105 return SCIP_INVALIDDATA;
9106 }
9107
9108 SCIPsetDebugMsg(set, "add local hole (%g,%g) to parent variable <%s>\n", parentnewleft, parentnewright, SCIPvarGetName(parentvar));
9109
9110 /* perform hole added for parent variable */
9111 assert(blkmem != NULL);
9112 assert(SCIPsetIsLT(set, parentnewleft, parentnewright));
9113 SCIP_CALL( varProcessAddHoleLocal(parentvar, blkmem, set, stat, eventqueue,
9114 parentnewleft, parentnewright, &localadded) );
9115 assert(localadded);
9116 }
9117
9118 return SCIP_OKAY;
9119 }
9120
9121 /** adds a hole to the variable's current local domain */
9122 SCIP_RETCODE SCIPvarAddHoleLocal(
9123 SCIP_VAR* var, /**< problem variable */
9124 BMS_BLKMEM* blkmem, /**< block memory */
9125 SCIP_SET* set, /**< global SCIP settings */
9126 SCIP_STAT* stat, /**< problem statistics */
9127 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
9128 SCIP_Real left, /**< left bound of open interval in new hole */
9129 SCIP_Real right, /**< right bound of open interval in new hole */
9130 SCIP_Bool* added /**< pointer to store whether the hole was added */
9131 )
9132 {
9133 SCIP_Real childnewleft;
9134 SCIP_Real childnewright;
9135
9136 assert(var != NULL);
9137
9138 SCIPsetDebugMsg(set, "adding local hole (%g,%g) to <%s>\n", left, right, var->name);
9139
9140 assert(set != NULL);
9141 assert(var->scip == set->scip);
9142 assert(SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS);
9143 assert(blkmem != NULL);
9144 assert(added != NULL);
9145
9146 /* the interval should not be empty */
9147 assert(SCIPsetIsLT(set, left, right));
9148
9149 /* the the interval bound should already be adjusted */
9150 assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarGetType(var), left)));
9151 assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarGetType(var), right)));
9152
9153 /* the the interval should lay between the lower and upper bound */
9154 assert(SCIPsetIsGE(set, left, SCIPvarGetLbLocal(var)));
9155 assert(SCIPsetIsLE(set, right, SCIPvarGetUbLocal(var)));
9156
9157 /* change bounds of attached variables */
9158 switch( SCIPvarGetStatus(var) )
9159 {
9160 case SCIP_VARSTATUS_ORIGINAL:
9161 if( var->data.original.transvar != NULL )
9162 {
9163 SCIP_CALL( SCIPvarAddHoleLocal(var->data.original.transvar, blkmem, set, stat, eventqueue,
9164 left, right, added) );
9165 }
9166 else
9167 {
9168 assert(set->stage == SCIP_STAGE_PROBLEM);
9169 SCIPstatIncrement(stat, set, domchgcount);
9170 SCIP_CALL( varProcessAddHoleLocal(var, blkmem, set, stat, eventqueue, left, right, added) );
9171 }
9172 break;
9173
9174 case SCIP_VARSTATUS_COLUMN:
9175 case SCIP_VARSTATUS_LOOSE:
9176 SCIPstatIncrement(stat, set, domchgcount);
9177 SCIP_CALL( varProcessAddHoleLocal(var, blkmem, set, stat, eventqueue, left, right, added) );
9178 break;
9179
9180 case SCIP_VARSTATUS_FIXED:
9181 SCIPerrorMessage("cannot add domain hole to a fixed variable\n");
9182 return SCIP_INVALIDDATA;
9183
9184 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
9185 assert(var->data.aggregate.var != NULL);
9186
9187 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
9188 {
9189 /* a > 0 -> change lower bound of y */
9190 childnewleft = (left - var->data.aggregate.constant)/var->data.aggregate.scalar;
9191 childnewright = (right - var->data.aggregate.constant)/var->data.aggregate.scalar;
9192 }
9193 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
9194 {
9195 childnewright = (left - var->data.aggregate.constant)/var->data.aggregate.scalar;
9196 childnewleft = (right - var->data.aggregate.constant)/var->data.aggregate.scalar;
9197 }
9198 else
9199 {
9200 SCIPerrorMessage("scalar is zero in aggregation\n");
9201 return SCIP_INVALIDDATA;
9202 }
9203 SCIP_CALL( SCIPvarAddHoleLocal(var->data.aggregate.var, blkmem, set, stat, eventqueue,
9204 childnewleft, childnewright, added) );
9205 break;
9206
9207 case SCIP_VARSTATUS_MULTAGGR:
9208 SCIPerrorMessage("cannot add domain hole to a multi-aggregated variable.\n");
9209 return SCIP_INVALIDDATA;
9210
9211 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
9212 assert(var->negatedvar != NULL);
9213 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
9214 assert(var->negatedvar->negatedvar == var);
9215
9216 childnewright = -left + var->data.negate.constant;
9217 childnewleft = -right + var->data.negate.constant;
9218
9219 SCIP_CALL( SCIPvarAddHoleLocal(var->negatedvar, blkmem, set, stat, eventqueue, childnewleft, childnewright, added) );
9220 break;
9221
9222 default:
9223 SCIPerrorMessage("unknown variable status\n");
9224 return SCIP_INVALIDDATA;
9225 }
9226
9227 return SCIP_OKAY;
9228 }
9229
9230 /** resets the global and local bounds of original variable to their original values */
9231 SCIP_RETCODE SCIPvarResetBounds(
9232 SCIP_VAR* var, /**< problem variable */
9233 BMS_BLKMEM* blkmem, /**< block memory */
9234 SCIP_SET* set, /**< global SCIP settings */
9235 SCIP_STAT* stat /**< problem statistics */
9236 )
9237 {
9238 assert(var != NULL);
9239 assert(set != NULL);
9240 assert(var->scip == set->scip);
9241 assert(SCIPvarIsOriginal(var));
9242 /* resetting of bounds on original variables which have a transformed counterpart easily fails if, e.g.,
9243 * the transformed variable has been fixed */
9244 assert(SCIPvarGetTransVar(var) == NULL);
9245
9246 /* copy the original bounds back to the global and local bounds */
9247 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, NULL, NULL, NULL, NULL, var->data.original.origdom.lb) );
9248 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, NULL, NULL, NULL, NULL, var->data.original.origdom.ub) );
9249 SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, NULL, NULL, NULL, var->data.original.origdom.lb) );
9250 SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, NULL, NULL, NULL, var->data.original.origdom.ub) );
9251
9252 /* free the global and local holelists and duplicate the original ones */
9253 /**@todo this has also to be called recursively with methods similar to SCIPvarChgLbGlobal() */
9254 holelistFree(&var->glbdom.holelist, blkmem);
9255 holelistFree(&var->locdom.holelist, blkmem);
9256 SCIP_CALL( holelistDuplicate(&var->glbdom.holelist, blkmem, set, var->data.original.origdom.holelist) );
9257 SCIP_CALL( holelistDuplicate(&var->locdom.holelist, blkmem, set, var->data.original.origdom.holelist) );
9258
9259 return SCIP_OKAY;
9260 }
9261
9262 /** issues a IMPLADDED event on the given variable */
9263 static
9264 SCIP_RETCODE varEventImplAdded(
9265 SCIP_VAR* var, /**< problem variable to change */
9266 BMS_BLKMEM* blkmem, /**< block memory */
9267 SCIP_SET* set, /**< global SCIP settings */
9268 SCIP_EVENTQUEUE* eventqueue /**< event queue */
9269 )
9270 {
9271 SCIP_EVENT* event;
9272
9273 assert(var != NULL);
9274
9275 /* issue IMPLADDED event on variable */
9276 SCIP_CALL( SCIPeventCreateImplAdded(&event, blkmem, var) );
9277 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, NULL, &event) );
9278
9279 return SCIP_OKAY;
9280 }
9281
9282 /** actually performs the addition of a variable bound to the variable's vbound arrays */
9283 static
9284 SCIP_RETCODE varAddVbound(
9285 SCIP_VAR* var, /**< problem variable x in x <= b*z + d or x >= b*z + d */
9286 BMS_BLKMEM* blkmem, /**< block memory */
9287 SCIP_SET* set, /**< global SCIP settings */
9288 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
9289 SCIP_BOUNDTYPE vbtype, /**< type of variable bound (LOWER or UPPER) */
9290 SCIP_VAR* vbvar, /**< variable z in x <= b*z + d or x >= b*z + d */
9291 SCIP_Real vbcoef, /**< coefficient b in x <= b*z + d or x >= b*z + d */
9292 SCIP_Real vbconstant /**< constant d in x <= b*z + d or x >= b*z + d */
9293 )
9294 {
9295 SCIP_Bool added;
9296
9297 /* It can happen that the variable "var" and the variable "vbvar" are the same variable. For example if a variable
9298 * gets aggregated, the variable bounds (vbound) of that variable are copied to the other variable. A variable bound
9299 * variable of the aggregated variable might be the same as the one its gets aggregated too.
9300 *
9301 * If the variable "var" and the variable "vbvar" are the same, the variable bound which should be added here has to
9302 * be redundant. This is the case since an infeasibility should have be detected in the previous methods. As well as
9303 * the bounds of the variable which should be also already be tightened in the previous methods. Therefore, the
9304 * variable bound can be ignored.
9305 *
9306 * From the way the the variable bound system is implemented (detecting infeasibility, tighten bounds), the
9307 * equivalence of the variables should be checked here.
9308 */
9309 if( var == vbvar )
9310 {
9311 /* in this case the variable bound has to be redundant, this means for possible assignments to this variable; this
9312 * can be checked via the global bounds of the variable */
9313 #ifndef NDEBUG
9314 SCIP_Real lb;
9315 SCIP_Real ub;
9316
9317 lb = SCIPvarGetLbGlobal(var);
9318 ub = SCIPvarGetUbGlobal(var);
9319
9320 if(vbtype == SCIP_BOUNDTYPE_LOWER)
9321 {
9322 if( vbcoef > 0.0 )
9323 {
9324 assert(SCIPsetIsGE(set, lb, lb * vbcoef + vbconstant) );
9325 assert(SCIPsetIsGE(set, ub, ub * vbcoef + vbconstant) );
9326 }
9327 else
9328 {
9329 assert(SCIPsetIsGE(set, lb, ub * vbcoef + vbconstant) );
9330 assert(SCIPsetIsGE(set, ub, lb * vbcoef + vbconstant) );
9331 }
9332 }
9333 else
9334 {
9335 assert(vbtype == SCIP_BOUNDTYPE_UPPER);
9336 if( vbcoef > 0.0 )
9337 {
9338 assert(SCIPsetIsLE(set, lb, lb * vbcoef + vbconstant) );
9339 assert(SCIPsetIsLE(set, ub, ub * vbcoef + vbconstant) );
9340 }
9341 else
9342 {
9343 assert(SCIPsetIsLE(set, lb, ub * vbcoef + vbconstant) );
9344 assert(SCIPsetIsLE(set, ub, lb * vbcoef + vbconstant) );
9345 }
9346 }
9347 #endif
9348 SCIPsetDebugMsg(set, "redundant variable bound: <%s> %s %g<%s> %+g\n",
9349 SCIPvarGetName(var), vbtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", vbcoef, SCIPvarGetName(vbvar), vbconstant);
9350
9351 return SCIP_OKAY;
9352 }
9353
9354 SCIPsetDebugMsg(set, "adding variable bound: <%s> %s %g<%s> %+g\n",
9355 SCIPvarGetName(var), vbtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", vbcoef, SCIPvarGetName(vbvar), vbconstant);
9356
9357 /* check variable bound on debugging solution */
9358 SCIP_CALL( SCIPdebugCheckVbound(set, var, vbtype, vbvar, vbcoef, vbconstant) ); /*lint !e506 !e774*/
9359
9360 /* perform the addition */
9361 if( vbtype == SCIP_BOUNDTYPE_LOWER )
9362 {
9363 SCIP_CALL( SCIPvboundsAdd(&var->vlbs, blkmem, set, vbtype, vbvar, vbcoef, vbconstant, &added) );
9364 }
9365 else
9366 {
9367 SCIP_CALL( SCIPvboundsAdd(&var->vubs, blkmem, set, vbtype, vbvar, vbcoef, vbconstant, &added) );
9368 }
9369 var->closestvblpcount = -1;
9370
9371 if( added )
9372 {
9373 /* issue IMPLADDED event */
9374 SCIP_CALL( varEventImplAdded(var, blkmem, set, eventqueue) );
9375 }
9376
9377 return SCIP_OKAY;
9378 }
9379
9380 /** checks whether the given implication is redundant or infeasible w.r.t. the implied variables global bounds */
9381 static
9382 void checkImplic(
9383 SCIP_SET* set, /**< global SCIP settings */
9384 SCIP_VAR* implvar, /**< variable y in implication y <= b or y >= b */
9385 SCIP_BOUNDTYPE impltype, /**< type of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
9386 SCIP_Real implbound, /**< bound b in implication y <= b or y >= b */
9387 SCIP_Bool* redundant, /**< pointer to store whether the implication is redundant */
9388 SCIP_Bool* infeasible /**< pointer to store whether the implication is infeasible */
9389 )
9390 {
9391 SCIP_Real impllb;
9392 SCIP_Real implub;
9393
9394 assert(redundant != NULL);
9395 assert(infeasible != NULL);
9396
9397 impllb = SCIPvarGetLbGlobal(implvar);
9398 implub = SCIPvarGetUbGlobal(implvar);
9399 if( impltype == SCIP_BOUNDTYPE_LOWER )
9400 {
9401 *infeasible = SCIPsetIsFeasGT(set, implbound, implub);
9402 *redundant = SCIPsetIsFeasLE(set, implbound, impllb);
9403 }
9404 else
9405 {
9406 *infeasible = SCIPsetIsFeasLT(set, implbound, impllb);
9407 *redundant = SCIPsetIsFeasGE(set, implbound, implub);
9408 }
9409 }
9410
9411 /** applies the given implication, if it is not redundant */
9412 static
9413 SCIP_RETCODE applyImplic(
9414 BMS_BLKMEM* blkmem, /**< block memory */
9415 SCIP_SET* set, /**< global SCIP settings */
9416 SCIP_STAT* stat, /**< problem statistics */
9417 SCIP_PROB* transprob, /**< transformed problem */
9418 SCIP_PROB* origprob, /**< original problem */
9419 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
9420 SCIP_REOPT* reopt, /**< reoptimization data structure */
9421 SCIP_LP* lp, /**< current LP data */
9422 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
9423 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
9424 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
9425 SCIP_VAR* implvar, /**< variable y in implication y <= b or y >= b */
9426 SCIP_BOUNDTYPE impltype, /**< type of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
9427 SCIP_Real implbound, /**< bound b in implication y <= b or y >= b */
9428 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
9429 int* nbdchgs /**< pointer to count the number of performed bound changes, or NULL */
9430 )
9431 {
9432 SCIP_Real implub;
9433 SCIP_Real impllb;
9434
9435 assert(infeasible != NULL);
9436
9437 *infeasible = FALSE;
9438
9439 implub = SCIPvarGetUbGlobal(implvar);
9440 impllb = SCIPvarGetLbGlobal(implvar);
9441 if( impltype == SCIP_BOUNDTYPE_LOWER )
9442 {
9443 if( SCIPsetIsFeasGT(set, implbound, implub) )
9444 {
9445 /* the implication produces a conflict: the problem is infeasible */
9446 *infeasible = TRUE;
9447 }
9448 else if( SCIPsetIsFeasGT(set, implbound, impllb) )
9449 {
9450 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
9451 * with the local bound, in this case we need to store the bound change as pending bound change
9452 */
9453 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
9454 {
9455 assert(tree != NULL);
9456 assert(transprob != NULL);
9457 assert(SCIPprobIsTransformed(transprob));
9458
9459 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
9460 tree, reopt, lp, branchcand, eventqueue, cliquetable, implvar, implbound, SCIP_BOUNDTYPE_LOWER, FALSE) );
9461 }
9462 else
9463 {
9464 SCIP_CALL( SCIPvarChgLbGlobal(implvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, implbound) );
9465 }
9466
9467 if( nbdchgs != NULL )
9468 (*nbdchgs)++;
9469 }
9470 }
9471 else
9472 {
9473 if( SCIPsetIsFeasLT(set, implbound, impllb) )
9474 {
9475 /* the implication produces a conflict: the problem is infeasible */
9476 *infeasible = TRUE;
9477 }
9478 else if( SCIPsetIsFeasLT(set, implbound, implub) )
9479 {
9480 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
9481 * with the local bound, in this case we need to store the bound change as pending bound change
9482 */
9483 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
9484 {
9485 assert(tree != NULL);
9486 assert(transprob != NULL);
9487 assert(SCIPprobIsTransformed(transprob));
9488
9489 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
9490 tree, reopt, lp, branchcand, eventqueue, cliquetable, implvar, implbound, SCIP_BOUNDTYPE_UPPER, FALSE) );
9491 }
9492 else
9493 {
9494 SCIP_CALL( SCIPvarChgUbGlobal(implvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, implbound) );
9495 }
9496
9497 if( nbdchgs != NULL )
9498 (*nbdchgs)++;
9499 }
9500 }
9501
9502 return SCIP_OKAY;
9503 }
9504
9505 /** actually performs the addition of an implication to the variable's implication arrays,
9506 * and adds the corresponding implication or variable bound to the implied variable;
9507 * if the implication is conflicting, the variable is fixed to the opposite value;
9508 * if the variable is already fixed to the given value, the implication is performed immediately;
9509 * if the implication is redundant with respect to the variables' global bounds, it is ignored
9510 */
9511 static
9512 SCIP_RETCODE varAddImplic(
9513 SCIP_VAR* var, /**< problem variable */
9514 BMS_BLKMEM* blkmem, /**< block memory */
9515 SCIP_SET* set, /**< global SCIP settings */
9516 SCIP_STAT* stat, /**< problem statistics */
9517 SCIP_PROB* transprob, /**< transformed problem */
9518 SCIP_PROB* origprob, /**< original problem */
9519 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
9520 SCIP_REOPT* reopt, /**< reoptimization data structure */
9521 SCIP_LP* lp, /**< current LP data */
9522 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
9523 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
9524 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
9525 SCIP_Bool varfixing, /**< FALSE if y should be added in implications for x == 0, TRUE for x == 1 */
9526 SCIP_VAR* implvar, /**< variable y in implication y <= b or y >= b */
9527 SCIP_BOUNDTYPE impltype, /**< type of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
9528 SCIP_Real implbound, /**< bound b in implication y <= b or y >= b */
9529 SCIP_Bool isshortcut, /**< is the implication a shortcut, i.e., added as part of the transitive closure of another implication? */
9530 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
9531 int* nbdchgs, /**< pointer to count the number of performed bound changes, or NULL */
9532 SCIP_Bool* added /**< pointer to store whether an implication was added */
9533 )
9534 {
9535 SCIP_Bool redundant;
9536 SCIP_Bool conflict;
9537
9538 assert(var != NULL);
9539 assert(SCIPvarIsActive(var));
9540 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
9541 assert(SCIPvarGetType(var) == SCIP_VARTYPE_BINARY);
9542 assert(SCIPvarIsActive(implvar) || SCIPvarGetStatus(implvar) == SCIP_VARSTATUS_FIXED);
9543 assert(infeasible != NULL);
9544 assert(added != NULL);
9545
9546 /* check implication on debugging solution */
9547 SCIP_CALL( SCIPdebugCheckImplic(set, var, varfixing, implvar, impltype, implbound) ); /*lint !e506 !e774*/
9548
9549 *infeasible = FALSE;
9550 *added = FALSE;
9551
9552 /* check, if the implication is redundant or infeasible */
9553 checkImplic(set, implvar, impltype, implbound, &redundant, &conflict);
9554 assert(!redundant || !conflict);
9555 if( redundant )
9556 return SCIP_OKAY;
9557
9558 if( var == implvar )
9559 {
9560 /* special cases appear were a bound to a variable implies itself to be outside the bounds:
9561 * x == varfixing => x < 0 or x > 1
9562 */
9563 if( SCIPsetIsLT(set, implbound, 0.0) || SCIPsetIsGT(set, implbound, 1.0) )
9564 conflict = TRUE;
9565 else
9566 {
9567 /* variable implies itself: x == varfixing => x == (impltype == SCIP_BOUNDTYPE_LOWER) */
9568 assert(SCIPsetIsZero(set, implbound) || SCIPsetIsEQ(set, implbound, 1.0));
9569 assert(SCIPsetIsZero(set, implbound) == (impltype == SCIP_BOUNDTYPE_UPPER));
9570 assert(SCIPsetIsEQ(set, implbound, 1.0) == (impltype == SCIP_BOUNDTYPE_LOWER));
9571 conflict = conflict || ((varfixing == TRUE) == (impltype == SCIP_BOUNDTYPE_UPPER));
9572 if( !conflict )
9573 return SCIP_OKAY;
9574 }
9575 }
9576
9577 /* check, if the variable is already fixed */
9578 if( SCIPvarGetLbGlobal(var) > 0.5 || SCIPvarGetUbGlobal(var) < 0.5 )
9579 {
9580 /* if the variable is fixed to the given value, perform the implication; otherwise, ignore the implication */
9581 if( varfixing == (SCIPvarGetLbGlobal(var) > 0.5) )
9582 {
9583 SCIP_CALL( applyImplic(blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, eventqueue,
9584 cliquetable, implvar, impltype, implbound, infeasible, nbdchgs) );
9585 }
9586 return SCIP_OKAY;
9587 }
9588
9589 assert((impltype == SCIP_BOUNDTYPE_LOWER && SCIPsetIsGT(set, implbound, SCIPvarGetLbGlobal(implvar)))
9590 || (impltype == SCIP_BOUNDTYPE_UPPER && SCIPsetIsLT(set, implbound, SCIPvarGetUbGlobal(implvar))));
9591
9592 if( !conflict )
9593 {
9594 assert(SCIPvarIsActive(implvar)); /* a fixed implvar would either cause a redundancy or infeasibility */
9595
9596 if( SCIPvarIsBinary(implvar) )
9597 {
9598 SCIP_VAR* vars[2];
9599 SCIP_Bool vals[2];
9600
9601 assert(SCIPsetIsFeasEQ(set, implbound, 1.0) || SCIPsetIsFeasZero(set, implbound));
9602 assert((impltype == SCIP_BOUNDTYPE_UPPER) == SCIPsetIsFeasZero(set, implbound));
9603
9604 vars[0] = var;
9605 vars[1] = implvar;
9606 vals[0] = varfixing;
9607 vals[1] = (impltype == SCIP_BOUNDTYPE_UPPER);
9608
9609 /* add the clique to the clique table */
9610 SCIP_CALL( SCIPcliquetableAdd(cliquetable, blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand,
9611 eventqueue, vars, vals, 2, FALSE, &conflict, nbdchgs) );
9612
9613 if( !conflict )
9614 return SCIP_OKAY;
9615 }
9616 else
9617 {
9618 /* add implication x == 0/1 -> y <= b / y >= b to the implications list of x */
9619 SCIPsetDebugMsg(set, "adding implication: <%s> == %u ==> <%s> %s %g\n",
9620 SCIPvarGetName(var), varfixing,
9621 SCIPvarGetName(implvar), impltype == SCIP_BOUNDTYPE_UPPER ? "<=" : ">=", implbound);
9622 SCIP_CALL( SCIPimplicsAdd(&var->implics, blkmem, set, stat, varfixing, implvar, impltype, implbound,
9623 isshortcut, &conflict, added) );
9624 }
9625 }
9626 assert(!conflict || !(*added));
9627
9628 /* on conflict, fix the variable to the opposite value */
9629 if( conflict )
9630 {
9631 SCIPsetDebugMsg(set, " -> implication yields a conflict: fix <%s> == %d\n", SCIPvarGetName(var), !varfixing);
9632
9633 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
9634 * with the local bound, in this case we need to store the bound change as pending bound change
9635 */
9636 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
9637 {
9638 assert(tree != NULL);
9639 assert(transprob != NULL);
9640 assert(SCIPprobIsTransformed(transprob));
9641
9642 if( varfixing )
9643 {
9644 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
9645 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, 0.0, SCIP_BOUNDTYPE_UPPER, FALSE) );
9646 }
9647 else
9648 {
9649 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
9650 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, 1.0, SCIP_BOUNDTYPE_LOWER, FALSE) );
9651 }
9652 }
9653 else
9654 {
9655 if( varfixing )
9656 {
9657 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, 0.0) );
9658 }
9659 else
9660 {
9661 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, 1.0) );
9662 }
9663 }
9664 if( nbdchgs != NULL )
9665 (*nbdchgs)++;
9666
9667 return SCIP_OKAY;
9668 }
9669 else if( *added )
9670 {
9671 /* issue IMPLADDED event */
9672 SCIP_CALL( varEventImplAdded(var, blkmem, set, eventqueue) );
9673 }
9674 else
9675 {
9676 /* the implication was redundant: the inverse is also redundant */
9677 return SCIP_OKAY;
9678 }
9679
9680 assert(SCIPvarIsActive(implvar)); /* a fixed implvar would either cause a redundancy or infeasibility */
9681
9682 /* check, whether implied variable is binary */
9683 if( !SCIPvarIsBinary(implvar) )
9684 {
9685 SCIP_Real lb;
9686 SCIP_Real ub;
9687
9688 /* add inverse variable bound to the variable bounds of y with global bounds y \in [lb,ub]:
9689 * x == 0 -> y <= b <-> y <= (ub - b)*x + b
9690 * x == 1 -> y <= b <-> y <= (b - ub)*x + ub
9691 * x == 0 -> y >= b <-> y >= (lb - b)*x + b
9692 * x == 1 -> y >= b <-> y >= (b - lb)*x + lb
9693 * for numerical reasons, ignore variable bounds with large absolute coefficient
9694 */
9695 lb = SCIPvarGetLbGlobal(implvar);
9696 ub = SCIPvarGetUbGlobal(implvar);
9697 if( impltype == SCIP_BOUNDTYPE_UPPER )
9698 {
9699 if( REALABS(implbound - ub) <= MAXABSVBCOEF )
9700 {
9701 SCIP_CALL( varAddVbound(implvar, blkmem, set, eventqueue, SCIP_BOUNDTYPE_UPPER, var,
9702 varfixing ? implbound - ub : ub - implbound, varfixing ? ub : implbound) );
9703 }
9704 }
9705 else
9706 {
9707 if( REALABS(implbound - lb) <= MAXABSVBCOEF )
9708 {
9709 SCIP_CALL( varAddVbound(implvar, blkmem, set, eventqueue, SCIP_BOUNDTYPE_LOWER, var,
9710 varfixing ? implbound - lb : lb - implbound, varfixing ? lb : implbound) );
9711 }
9712 }
9713 }
9714
9715 return SCIP_OKAY;
9716 }
9717
9718 /** adds transitive closure for binary implication x = a -> y = b */
9719 static
9720 SCIP_RETCODE varAddTransitiveBinaryClosureImplic(
9721 SCIP_VAR* var, /**< problem variable */
9722 BMS_BLKMEM* blkmem, /**< block memory */
9723 SCIP_SET* set, /**< global SCIP settings */
9724 SCIP_STAT* stat, /**< problem statistics */
9725 SCIP_PROB* transprob, /**< transformed problem */
9726 SCIP_PROB* origprob, /**< original problem */
9727 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
9728 SCIP_REOPT* reopt, /**< reoptimization data structure */
9729 SCIP_LP* lp, /**< current LP data */
9730 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
9731 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
9732 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
9733 SCIP_Bool varfixing, /**< FALSE if y should be added in implications for x == 0, TRUE for x == 1 */
9734 SCIP_VAR* implvar, /**< variable y in implication y <= b or y >= b */
9735 SCIP_Bool implvarfixing, /**< fixing b in implication */
9736 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
9737 int* nbdchgs /**< pointer to count the number of performed bound changes, or NULL */
9738 )
9739 {
9740 SCIP_VAR** implvars;
9741 SCIP_BOUNDTYPE* impltypes;
9742 SCIP_Real* implbounds;
9743 int nimpls;
9744 int i;
9745
9746 *infeasible = FALSE;
9747
9748 /* binary variable: implications of implvar */
9749 nimpls = SCIPimplicsGetNImpls(implvar->implics, implvarfixing);
9750 implvars = SCIPimplicsGetVars(implvar->implics, implvarfixing);
9751 impltypes = SCIPimplicsGetTypes(implvar->implics, implvarfixing);
9752 implbounds = SCIPimplicsGetBounds(implvar->implics, implvarfixing);
9753
9754 /* if variable has too many implications, the implication graph may become too dense */
9755 i = MIN(nimpls, MAXIMPLSCLOSURE) - 1;
9756
9757 /* we have to iterate from back to front, because in varAddImplic() it may happen that a conflict is detected and
9758 * implvars[i] is fixed, s.t. the implication y == varfixing -> z <= b / z >= b is deleted; this affects the
9759 * array over which we currently iterate; the only thing that can happen, is that elements of the array are
9760 * deleted; in this case, the subsequent elements are moved to the front; if we iterate from back to front, the
9761 * only thing that can happen is that we add the same implication twice - this does no harm
9762 */
9763 while ( i >= 0 && !(*infeasible) )
9764 {
9765 SCIP_Bool added;
9766
9767 assert(implvars[i] != implvar);
9768
9769 /* we have x == varfixing -> y == implvarfixing -> z <= b / z >= b:
9770 * add implication x == varfixing -> z <= b / z >= b to the implications list of x
9771 */
9772 if( SCIPvarIsActive(implvars[i]) )
9773 {
9774 SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable, branchcand,
9775 eventqueue, varfixing, implvars[i], impltypes[i], implbounds[i], TRUE, infeasible, nbdchgs, &added) );
9776 assert(SCIPimplicsGetNImpls(implvar->implics, implvarfixing) <= nimpls);
9777 nimpls = SCIPimplicsGetNImpls(implvar->implics, implvarfixing);
9778 i = MIN(i, nimpls); /* some elements from the array could have been removed */
9779 }
9780 --i;
9781 }
9782
9783 return SCIP_OKAY;
9784 }
9785
9786 /** adds given implication to the variable's implication list, and adds all implications directly implied by this
9787 * implication to the variable's implication list;
9788 * if the implication is conflicting, the variable is fixed to the opposite value;
9789 * if the variable is already fixed to the given value, the implication is performed immediately;
9790 * if the implication is redundant with respect to the variables' global bounds, it is ignored
9791 */
9792 static
9793 SCIP_RETCODE varAddTransitiveImplic(
9794 SCIP_VAR* var, /**< problem variable */
9795 BMS_BLKMEM* blkmem, /**< block memory */
9796 SCIP_SET* set, /**< global SCIP settings */
9797 SCIP_STAT* stat, /**< problem statistics */
9798 SCIP_PROB* transprob, /**< transformed problem */
9799 SCIP_PROB* origprob, /**< original problem */
9800 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
9801 SCIP_REOPT* reopt, /**< reoptimization data structure */
9802 SCIP_LP* lp, /**< current LP data */
9803 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
9804 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
9805 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
9806 SCIP_Bool varfixing, /**< FALSE if y should be added in implications for x == 0, TRUE for x == 1 */
9807 SCIP_VAR* implvar, /**< variable y in implication y <= b or y >= b */
9808 SCIP_BOUNDTYPE impltype, /**< type of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
9809 SCIP_Real implbound, /**< bound b in implication y <= b or y >= b */
9810 SCIP_Bool transitive, /**< should transitive closure of implication also be added? */
9811 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
9812 int* nbdchgs /**< pointer to count the number of performed bound changes, or NULL */
9813 )
9814 {
9815 SCIP_Bool added;
9816
9817 assert(var != NULL);
9818 assert(SCIPvarGetType(var) == SCIP_VARTYPE_BINARY);
9819 assert(SCIPvarIsActive(var));
9820 assert(implvar != NULL);
9821 assert(SCIPvarIsActive(implvar) || SCIPvarGetStatus(implvar) == SCIP_VARSTATUS_FIXED);
9822 assert(infeasible != NULL);
9823
9824 /* add implication x == varfixing -> y <= b / y >= b to the implications list of x */
9825 SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable, branchcand,
9826 eventqueue, varfixing, implvar, impltype, implbound, FALSE, infeasible, nbdchgs, &added) );
9827
9828 if( *infeasible || var == implvar || !transitive || !added )
9829 return SCIP_OKAY;
9830
9831 assert(SCIPvarIsActive(implvar)); /* a fixed implvar would either cause a redundancy or infeasibility */
9832
9833 /* add transitive closure */
9834 if( SCIPvarGetType(implvar) == SCIP_VARTYPE_BINARY )
9835 {
9836 SCIP_Bool implvarfixing;
9837
9838 implvarfixing = (impltype == SCIP_BOUNDTYPE_LOWER);
9839
9840 /* binary variable: implications of implvar */
9841 SCIP_CALL( varAddTransitiveBinaryClosureImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
9842 cliquetable, branchcand, eventqueue, varfixing, implvar, implvarfixing, infeasible, nbdchgs) );
9843
9844 /* inverse implication */
9845 if( !(*infeasible) )
9846 {
9847 SCIP_CALL( varAddTransitiveBinaryClosureImplic(implvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
9848 cliquetable, branchcand, eventqueue, !implvarfixing, var, !varfixing, infeasible, nbdchgs) );
9849 }
9850 }
9851 else
9852 {
9853 /* non-binary variable: variable lower bounds of implvar */
9854 if( impltype == SCIP_BOUNDTYPE_UPPER && implvar->vlbs != NULL )
9855 {
9856 SCIP_VAR** vlbvars;
9857 SCIP_Real* vlbcoefs;
9858 SCIP_Real* vlbconstants;
9859 int nvlbvars;
9860 int i;
9861
9862 nvlbvars = SCIPvboundsGetNVbds(implvar->vlbs);
9863 vlbvars = SCIPvboundsGetVars(implvar->vlbs);
9864 vlbcoefs = SCIPvboundsGetCoefs(implvar->vlbs);
9865 vlbconstants = SCIPvboundsGetConstants(implvar->vlbs);
9866
9867 /* we have to iterate from back to front, because in varAddImplic() it may happen that a conflict is detected and
9868 * vlbvars[i] is fixed, s.t. the variable bound is deleted; this affects the array over which we currently
9869 * iterate; the only thing that can happen, is that elements of the array are deleted; in this case, the
9870 * subsequent elements are moved to the front; if we iterate from back to front, the only thing that can happen
9871 * is that we add the same implication twice - this does no harm
9872 */
9873 i = nvlbvars-1;
9874 while ( i >= 0 && !(*infeasible) )
9875 {
9876 assert(vlbvars[i] != implvar);
9877 assert(!SCIPsetIsZero(set, vlbcoefs[i]));
9878
9879 /* we have x == varfixing -> y <= b and y >= c*z + d:
9880 * c > 0: add implication x == varfixing -> z <= (b-d)/c to the implications list of x
9881 * c < 0: add implication x == varfixing -> z >= (b-d)/c to the implications list of x
9882 *
9883 * @note during an aggregation the aggregated variable "aggrvar" (the one which will have the status
9884 * SCIP_VARSTATUS_AGGREGATED afterwards) copies its variable lower and uppers bounds to the
9885 * aggregation variable (the one which will stay active);
9886 *
9887 * W.l.o.g. we consider the variable upper bounds for now. Let "vubvar" be a variable upper bound of
9888 * the aggregated variable "aggvar"; During that copying of that variable upper bound variable
9889 * "vubvar" the variable lower and upper bounds of this variable "vubvar" are also considered; note
9890 * that the "aggvar" can be a variable lower bound variable of the variable "vubvar"; Due to that
9891 * situation it can happen that we reach that code place where "vlbvars[i] == aggvar". In particular
9892 * the "aggvar" has already the variable status SCIP_VARSTATUS_AGGREGATED or SCIP_VARSTATUS_NEGATED
9893 * but is still active since the aggregation is not finished yet (in SCIPvarAggregate()); therefore we
9894 * have to explicitly check that the active variable has not a variable status
9895 * SCIP_VARSTATUS_AGGREGATED or SCIP_VARSTATUS_NEGATED;
9896 */
9897 if( SCIPvarIsActive(vlbvars[i]) && SCIPvarGetStatus(vlbvars[i]) != SCIP_VARSTATUS_AGGREGATED && SCIPvarGetStatus(vlbvars[i]) != SCIP_VARSTATUS_NEGATED )
9898 {
9899 SCIP_Real vbimplbound;
9900
9901 vbimplbound = (implbound - vlbconstants[i])/vlbcoefs[i];
9902 if( vlbcoefs[i] >= 0.0 )
9903 {
9904 vbimplbound = adjustedUb(set, SCIPvarGetType(vlbvars[i]), vbimplbound);
9905 SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
9906 branchcand, eventqueue, varfixing, vlbvars[i], SCIP_BOUNDTYPE_UPPER, vbimplbound, TRUE,
9907 infeasible, nbdchgs, &added) );
9908 }
9909 else
9910 {
9911 vbimplbound = adjustedLb(set, SCIPvarGetType(vlbvars[i]), vbimplbound);
9912 SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
9913 branchcand, eventqueue, varfixing, vlbvars[i], SCIP_BOUNDTYPE_LOWER, vbimplbound, TRUE,
9914 infeasible, nbdchgs, &added) );
9915 }
9916 nvlbvars = SCIPvboundsGetNVbds(implvar->vlbs);
9917 i = MIN(i, nvlbvars); /* some elements from the array could have been removed */
9918 }
9919 --i;
9920 }
9921 }
9922
9923 /* non-binary variable: variable upper bounds of implvar */
9924 if( impltype == SCIP_BOUNDTYPE_LOWER && implvar->vubs != NULL )
9925 {
9926 SCIP_VAR** vubvars;
9927 SCIP_Real* vubcoefs;
9928 SCIP_Real* vubconstants;
9929 int nvubvars;
9930 int i;
9931
9932 nvubvars = SCIPvboundsGetNVbds(implvar->vubs);
9933 vubvars = SCIPvboundsGetVars(implvar->vubs);
9934 vubcoefs = SCIPvboundsGetCoefs(implvar->vubs);
9935 vubconstants = SCIPvboundsGetConstants(implvar->vubs);
9936
9937 /* we have to iterate from back to front, because in varAddImplic() it may happen that a conflict is detected and
9938 * vubvars[i] is fixed, s.t. the variable bound is deleted; this affects the array over which we currently
9939 * iterate; the only thing that can happen, is that elements of the array are deleted; in this case, the
9940 * subsequent elements are moved to the front; if we iterate from back to front, the only thing that can happen
9941 * is that we add the same implication twice - this does no harm
9942 */
9943 i = nvubvars-1;
9944 while ( i >= 0 && !(*infeasible) )
9945 {
9946 assert(vubvars[i] != implvar);
9947 assert(!SCIPsetIsZero(set, vubcoefs[i]));
9948
9949 /* we have x == varfixing -> y >= b and y <= c*z + d:
9950 * c > 0: add implication x == varfixing -> z >= (b-d)/c to the implications list of x
9951 * c < 0: add implication x == varfixing -> z <= (b-d)/c to the implications list of x
9952 *
9953 * @note during an aggregation the aggregated variable "aggrvar" (the one which will have the status
9954 * SCIP_VARSTATUS_AGGREGATED afterwards) copies its variable lower and uppers bounds to the
9955 * aggregation variable (the one which will stay active);
9956 *
9957 * W.l.o.g. we consider the variable lower bounds for now. Let "vlbvar" be a variable lower bound of
9958 * the aggregated variable "aggvar"; During that copying of that variable lower bound variable
9959 * "vlbvar" the variable lower and upper bounds of this variable "vlbvar" are also considered; note
9960 * that the "aggvar" can be a variable upper bound variable of the variable "vlbvar"; Due to that
9961 * situation it can happen that we reach that code place where "vubvars[i] == aggvar". In particular
9962 * the "aggvar" has already the variable status SCIP_VARSTATUS_AGGREGATED or SCIP_VARSTATUS_NEGATED
9963 * but is still active since the aggregation is not finished yet (in SCIPvarAggregate()); therefore we
9964 * have to explicitly check that the active variable has not a variable status
9965 * SCIP_VARSTATUS_AGGREGATED or SCIP_VARSTATUS_NEGATED;
9966 */
9967 if( SCIPvarIsActive(vubvars[i]) && SCIPvarGetStatus(vubvars[i]) != SCIP_VARSTATUS_AGGREGATED && SCIPvarGetStatus(vubvars[i]) != SCIP_VARSTATUS_NEGATED )
9968 {
9969 SCIP_Real vbimplbound;
9970
9971 vbimplbound = (implbound - vubconstants[i])/vubcoefs[i];
9972 if( vubcoefs[i] >= 0.0 )
9973 {
9974 vbimplbound = adjustedLb(set, SCIPvarGetType(vubvars[i]), vbimplbound);
9975 SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
9976 branchcand, eventqueue, varfixing, vubvars[i], SCIP_BOUNDTYPE_LOWER, vbimplbound, TRUE,
9977 infeasible, nbdchgs, &added) );
9978 }
9979 else
9980 {
9981 vbimplbound = adjustedUb(set, SCIPvarGetType(vubvars[i]), vbimplbound);
9982 SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
9983 branchcand, eventqueue, varfixing, vubvars[i], SCIP_BOUNDTYPE_UPPER, vbimplbound, TRUE,
9984 infeasible, nbdchgs, &added) );
9985 }
9986 nvubvars = SCIPvboundsGetNVbds(implvar->vubs);
9987 i = MIN(i, nvubvars); /* some elements from the array could have been removed */
9988 }
9989 --i;
9990 }
9991 }
9992 }
9993
9994 return SCIP_OKAY;
9995 }
9996
9997 /** informs variable x about a globally valid variable lower bound x >= b*z + d with integer variable z;
9998 * if z is binary, the corresponding valid implication for z is also added;
9999 * improves the global bounds of the variable and the vlb variable if possible
10000 */
10001 SCIP_RETCODE SCIPvarAddVlb(
10002 SCIP_VAR* var, /**< problem variable */
10003 BMS_BLKMEM* blkmem, /**< block memory */
10004 SCIP_SET* set, /**< global SCIP settings */
10005 SCIP_STAT* stat, /**< problem statistics */
10006 SCIP_PROB* transprob, /**< transformed problem */
10007 SCIP_PROB* origprob, /**< original problem */
10008 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
10009 SCIP_REOPT* reopt, /**< reoptimization data structure */
10010 SCIP_LP* lp, /**< current LP data */
10011 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
10012 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
10013 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
10014 SCIP_VAR* vlbvar, /**< variable z in x >= b*z + d */
10015 SCIP_Real vlbcoef, /**< coefficient b in x >= b*z + d */
10016 SCIP_Real vlbconstant, /**< constant d in x >= b*z + d */
10017 SCIP_Bool transitive, /**< should transitive closure of implication also be added? */
10018 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
10019 int* nbdchgs /**< pointer to store the number of performed bound changes, or NULL */
10020 )
10021 {
10022 assert(var != NULL);
10023 assert(set != NULL);
10024 assert(var->scip == set->scip);
10025 assert(SCIPvarGetType(vlbvar) != SCIP_VARTYPE_CONTINUOUS);
10026 assert(infeasible != NULL);
10027
10028 SCIPsetDebugMsg(set, "adding variable lower bound <%s> >= %g<%s> + %g\n", SCIPvarGetName(var), vlbcoef, SCIPvarGetName(vlbvar), vlbconstant);
10029
10030 *infeasible = FALSE;
10031 if( nbdchgs != NULL )
10032 *nbdchgs = 0;
10033
10034 switch( SCIPvarGetStatus(var) )
10035 {
10036 case SCIP_VARSTATUS_ORIGINAL:
10037 assert(var->data.original.transvar != NULL);
10038 SCIP_CALL( SCIPvarAddVlb(var->data.original.transvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10039 cliquetable, branchcand, eventqueue, vlbvar, vlbcoef, vlbconstant, transitive, infeasible, nbdchgs) );
10040 break;
10041
10042 case SCIP_VARSTATUS_COLUMN:
10043 case SCIP_VARSTATUS_LOOSE:
10044 case SCIP_VARSTATUS_FIXED:
10045 /* transform b*z + d into the corresponding sum after transforming z to an active problem variable */
10046 SCIP_CALL( SCIPvarGetProbvarSum(&vlbvar, set, &vlbcoef, &vlbconstant) );
10047 SCIPsetDebugMsg(set, " -> transformed to variable lower bound <%s> >= %g<%s> + %g\n", SCIPvarGetName(var), vlbcoef, SCIPvarGetName(vlbvar), vlbconstant);
10048
10049 /* if the vlb coefficient is zero, just update the lower bound of the variable */
10050 if( SCIPsetIsZero(set, vlbcoef) )
10051 {
10052 if( SCIPsetIsFeasGT(set, vlbconstant, SCIPvarGetUbGlobal(var)) )
10053 *infeasible = TRUE;
10054 else if( SCIPsetIsFeasGT(set, vlbconstant, SCIPvarGetLbGlobal(var)) )
10055 {
10056 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10057 * with the local bound, in this case we need to store the bound change as pending bound change
10058 */
10059 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10060 {
10061 assert(tree != NULL);
10062 assert(transprob != NULL);
10063 assert(SCIPprobIsTransformed(transprob));
10064
10065 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10066 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, vlbconstant, SCIP_BOUNDTYPE_LOWER, FALSE) );
10067 }
10068 else
10069 {
10070 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, vlbconstant) );
10071 }
10072
10073 if( nbdchgs != NULL )
10074 (*nbdchgs)++;
10075 }
10076 }
10077 else if( var == vlbvar )
10078 {
10079 /* the variables cancels out, the variable bound constraint is either redundant or proves global infeasibility */
10080 if( SCIPsetIsEQ(set, vlbcoef, 1.0) )
10081 {
10082 if( SCIPsetIsPositive(set, vlbconstant) )
10083 *infeasible = TRUE;
10084 return SCIP_OKAY;
10085 }
10086 else
10087 {
10088 SCIP_Real lb = SCIPvarGetLbGlobal(var);
10089 SCIP_Real ub = SCIPvarGetUbGlobal(var);
10090
10091 /* the variable bound constraint defines a new upper bound */
10092 if( SCIPsetIsGT(set, vlbcoef, 1.0) )
10093 {
10094 SCIP_Real newub = vlbconstant / (1.0 - vlbcoef);
10095
10096 if( SCIPsetIsFeasLT(set, newub, lb) )
10097 {
10098 *infeasible = TRUE;
10099 return SCIP_OKAY;
10100 }
10101 else if( SCIPsetIsFeasLT(set, newub, ub) )
10102 {
10103 /* bound might be adjusted due to integrality condition */
10104 newub = adjustedUb(set, SCIPvarGetType(var), newub);
10105
10106 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10107 * with the local bound, in this case we need to store the bound change as pending bound change
10108 */
10109 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10110 {
10111 assert(tree != NULL);
10112 assert(transprob != NULL);
10113 assert(SCIPprobIsTransformed(transprob));
10114
10115 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10116 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, newub, SCIP_BOUNDTYPE_UPPER, FALSE) );
10117 }
10118 else
10119 {
10120 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newub) );
10121 }
10122
10123 if( nbdchgs != NULL )
10124 (*nbdchgs)++;
10125 }
10126 }
10127 /* the variable bound constraint defines a new lower bound */
10128 else
10129 {
10130 SCIP_Real newlb;
10131
10132 assert(SCIPsetIsLT(set, vlbcoef, 1.0));
10133
10134 newlb = vlbconstant / (1.0 - vlbcoef);
10135
10136 if( SCIPsetIsFeasGT(set, newlb, ub) )
10137 {
10138 *infeasible = TRUE;
10139 return SCIP_OKAY;
10140 }
10141 else if( SCIPsetIsFeasGT(set, newlb, lb) )
10142 {
10143 /* bound might be adjusted due to integrality condition */
10144 newlb = adjustedLb(set, SCIPvarGetType(var), newlb);
10145
10146 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10147 * with the local bound, in this case we need to store the bound change as pending bound change
10148 */
10149 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10150 {
10151 assert(tree != NULL);
10152 assert(transprob != NULL);
10153 assert(SCIPprobIsTransformed(transprob));
10154
10155 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10156 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, newlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
10157 }
10158 else
10159 {
10160 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newlb) );
10161 }
10162
10163 if( nbdchgs != NULL )
10164 (*nbdchgs)++;
10165 }
10166 }
10167 }
10168 }
10169 else if( SCIPvarIsActive(vlbvar) )
10170 {
10171 SCIP_Real xlb;
10172 SCIP_Real xub;
10173 SCIP_Real zlb;
10174 SCIP_Real zub;
10175 SCIP_Real minvlb;
10176 SCIP_Real maxvlb;
10177
10178 assert(SCIPvarGetStatus(vlbvar) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(vlbvar) == SCIP_VARSTATUS_COLUMN);
10179 assert(vlbcoef != 0.0);
10180
10181 minvlb = -SCIPsetInfinity(set);
10182 maxvlb = SCIPsetInfinity(set);
10183
10184 xlb = SCIPvarGetLbGlobal(var);
10185 xub = SCIPvarGetUbGlobal(var);
10186 zlb = SCIPvarGetLbGlobal(vlbvar);
10187 zub = SCIPvarGetUbGlobal(vlbvar);
10188
10189 /* improve global bounds of vlb variable, and calculate minimal and maximal value of variable bound */
10190 if( vlbcoef >= 0.0 )
10191 {
10192 SCIP_Real newzub;
10193
10194 if( !SCIPsetIsInfinity(set, xub) )
10195 {
10196 /* x >= b*z + d -> z <= (x-d)/b */
10197 newzub = (xub - vlbconstant)/vlbcoef;
10198
10199 /* return if the new bound is less than -infinity */
10200 if( SCIPsetIsInfinity(set, REALABS(newzub)) )
10201 return SCIP_OKAY;
10202
10203 if( SCIPsetIsFeasLT(set, newzub, zlb) )
10204 {
10205 *infeasible = TRUE;
10206 return SCIP_OKAY;
10207 }
10208 if( SCIPsetIsFeasLT(set, newzub, zub) )
10209 {
10210 /* bound might be adjusted due to integrality condition */
10211 newzub = adjustedUb(set, SCIPvarGetType(vlbvar), newzub);
10212
10213 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10214 * with the local bound, in this case we need to store the bound change as pending bound change
10215 */
10216 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10217 {
10218 assert(tree != NULL);
10219 assert(transprob != NULL);
10220 assert(SCIPprobIsTransformed(transprob));
10221
10222 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10223 tree, reopt, lp, branchcand, eventqueue, cliquetable, vlbvar, newzub, SCIP_BOUNDTYPE_UPPER, FALSE) );
10224 }
10225 else
10226 {
10227 SCIP_CALL( SCIPvarChgUbGlobal(vlbvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newzub) );
10228 }
10229 zub = newzub;
10230
10231 if( nbdchgs != NULL )
10232 (*nbdchgs)++;
10233 }
10234 maxvlb = vlbcoef * zub + vlbconstant;
10235 if( !SCIPsetIsInfinity(set, -zlb) )
10236 minvlb = vlbcoef * zlb + vlbconstant;
10237 }
10238 else
10239 {
10240 if( !SCIPsetIsInfinity(set, zub) )
10241 maxvlb = vlbcoef * zub + vlbconstant;
10242 if( !SCIPsetIsInfinity(set, -zlb) )
10243 minvlb = vlbcoef * zlb + vlbconstant;
10244 }
10245 }
10246 else
10247 {
10248 SCIP_Real newzlb;
10249
10250 if( !SCIPsetIsInfinity(set, xub) )
10251 {
10252 /* x >= b*z + d -> z >= (x-d)/b */
10253 newzlb = (xub - vlbconstant)/vlbcoef;
10254
10255 /* return if the new bound is larger than infinity */
10256 if( SCIPsetIsInfinity(set, REALABS(newzlb)) )
10257 return SCIP_OKAY;
10258
10259 if( SCIPsetIsFeasGT(set, newzlb, zub) )
10260 {
10261 *infeasible = TRUE;
10262 return SCIP_OKAY;
10263 }
10264 if( SCIPsetIsFeasGT(set, newzlb, zlb) )
10265 {
10266 /* bound might be adjusted due to integrality condition */
10267 newzlb = adjustedLb(set, SCIPvarGetType(vlbvar), newzlb);
10268
10269 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10270 * with the local bound, in this case we need to store the bound change as pending bound change
10271 */
10272 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10273 {
10274 assert(tree != NULL);
10275 assert(transprob != NULL);
10276 assert(SCIPprobIsTransformed(transprob));
10277
10278 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10279 tree, reopt, lp, branchcand, eventqueue, cliquetable, vlbvar, newzlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
10280 }
10281 else
10282 {
10283 SCIP_CALL( SCIPvarChgLbGlobal(vlbvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newzlb) );
10284 }
10285 zlb = newzlb;
10286
10287 if( nbdchgs != NULL )
10288 (*nbdchgs)++;
10289 }
10290 maxvlb = vlbcoef * zlb + vlbconstant;
10291 if( !SCIPsetIsInfinity(set, zub) )
10292 minvlb = vlbcoef * zub + vlbconstant;
10293 }
10294 else
10295 {
10296 if( !SCIPsetIsInfinity(set, -zlb) )
10297 maxvlb = vlbcoef * zlb + vlbconstant;
10298 if( !SCIPsetIsInfinity(set, zub) )
10299 minvlb = vlbcoef * zub + vlbconstant;
10300 }
10301 }
10302 if( maxvlb < minvlb )
10303 maxvlb = minvlb;
10304
10305 /* adjust bounds due to integrality of variable */
10306 minvlb = adjustedLb(set, SCIPvarGetType(var), minvlb);
10307 maxvlb = adjustedLb(set, SCIPvarGetType(var), maxvlb);
10308
10309 /* check bounds for feasibility */
10310 if( SCIPsetIsFeasGT(set, minvlb, xub) || (var == vlbvar && SCIPsetIsEQ(set, vlbcoef, 1.0) && SCIPsetIsFeasPositive(set, vlbconstant)) )
10311 {
10312 *infeasible = TRUE;
10313 return SCIP_OKAY;
10314 }
10315 /* improve global lower bound of variable */
10316 if( SCIPsetIsFeasGT(set, minvlb, xlb) )
10317 {
10318 /* bound might be adjusted due to integrality condition */
10319 minvlb = adjustedLb(set, SCIPvarGetType(var), minvlb);
10320
10321 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10322 * with the local bound, in this case we need to store the bound change as pending bound change
10323 */
10324 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10325 {
10326 assert(tree != NULL);
10327 assert(transprob != NULL);
10328 assert(SCIPprobIsTransformed(transprob));
10329
10330 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10331 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, minvlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
10332 }
10333 else
10334 {
10335 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, minvlb) );
10336 }
10337 xlb = minvlb;
10338
10339 if( nbdchgs != NULL )
10340 (*nbdchgs)++;
10341 }
10342 minvlb = xlb;
10343
10344 /* improve variable bound for binary z by moving the variable's global bound to the vlb constant */
10345 if( SCIPvarGetType(vlbvar) == SCIP_VARTYPE_BINARY )
10346 {
10347 /* b > 0: x >= (maxvlb - minvlb) * z + minvlb
10348 * b < 0: x >= (minvlb - maxvlb) * z + maxvlb
10349 */
10350
10351 assert(!SCIPsetIsInfinity(set, maxvlb) && !SCIPsetIsInfinity(set, -minvlb));
10352
10353 if( vlbcoef >= 0.0 )
10354 {
10355 vlbcoef = maxvlb - minvlb;
10356 vlbconstant = minvlb;
10357 }
10358 else
10359 {
10360 vlbcoef = minvlb - maxvlb;
10361 vlbconstant = maxvlb;
10362 }
10363 }
10364
10365 /* add variable bound to the variable bounds list */
10366 if( SCIPsetIsFeasGT(set, maxvlb, xlb) )
10367 {
10368 assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_FIXED);
10369 assert(!SCIPsetIsZero(set, vlbcoef));
10370
10371 /* if one of the variables is binary, add the corresponding implication to the variable's implication
10372 * list, thereby also adding the variable bound (or implication) to the other variable
10373 */
10374 if( SCIPvarGetType(vlbvar) == SCIP_VARTYPE_BINARY )
10375 {
10376 /* add corresponding implication:
10377 * b > 0, x >= b*z + d <-> z == 1 -> x >= b+d
10378 * b < 0, x >= b*z + d <-> z == 0 -> x >= d
10379 */
10380 SCIP_CALL( varAddTransitiveImplic(vlbvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10381 cliquetable, branchcand, eventqueue, (vlbcoef >= 0.0), var, SCIP_BOUNDTYPE_LOWER, maxvlb, transitive,
10382 infeasible, nbdchgs) );
10383 }
10384 else if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY )
10385 {
10386 /* add corresponding implication:
10387 * b > 0, x >= b*z + d <-> x == 0 -> z <= -d/b
10388 * b < 0, x >= b*z + d <-> x == 0 -> z >= -d/b
10389 */
10390 SCIP_Real implbound;
10391 implbound = -vlbconstant/vlbcoef;
10392
10393 /* tighten the implication bound if the variable is integer */
10394 if( SCIPvarIsIntegral(vlbvar) )
10395 {
10396 if( vlbcoef >= 0 )
10397 implbound = SCIPsetFloor(set, implbound);
10398 else
10399 implbound = SCIPsetCeil(set, implbound);
10400 }
10401 SCIP_CALL( varAddTransitiveImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10402 cliquetable, branchcand, eventqueue, FALSE, vlbvar, (vlbcoef >= 0.0 ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER),
10403 implbound, transitive, infeasible, nbdchgs) );
10404 }
10405 else
10406 {
10407 SCIP_CALL( varAddVbound(var, blkmem, set, eventqueue, SCIP_BOUNDTYPE_LOWER, vlbvar, vlbcoef, vlbconstant) );
10408 }
10409 }
10410 }
10411 break;
10412
10413 case SCIP_VARSTATUS_AGGREGATED:
10414 /* x = a*y + c: x >= b*z + d <=> a*y + c >= b*z + d <=> y >= b/a * z + (d-c)/a, if a > 0
10415 * y <= b/a * z + (d-c)/a, if a < 0
10416 */
10417 assert(var->data.aggregate.var != NULL);
10418 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
10419 {
10420 /* a > 0 -> add variable lower bound */
10421 SCIP_CALL( SCIPvarAddVlb(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10422 cliquetable, branchcand, eventqueue, vlbvar, vlbcoef/var->data.aggregate.scalar,
10423 (vlbconstant - var->data.aggregate.constant)/var->data.aggregate.scalar, transitive, infeasible, nbdchgs) );
10424 }
10425 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
10426 {
10427 /* a < 0 -> add variable upper bound */
10428 SCIP_CALL( SCIPvarAddVub(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10429 cliquetable, branchcand, eventqueue, vlbvar, vlbcoef/var->data.aggregate.scalar,
10430 (vlbconstant - var->data.aggregate.constant)/var->data.aggregate.scalar, transitive, infeasible, nbdchgs) );
10431 }
10432 else
10433 {
10434 SCIPerrorMessage("scalar is zero in aggregation\n");
10435 return SCIP_INVALIDDATA;
10436 }
10437 break;
10438
10439 case SCIP_VARSTATUS_MULTAGGR:
10440 /* nothing to do here */
10441 break;
10442
10443 case SCIP_VARSTATUS_NEGATED:
10444 /* x = offset - x': x >= b*z + d <=> offset - x' >= b*z + d <=> x' <= -b*z + (offset-d) */
10445 assert(var->negatedvar != NULL);
10446 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
10447 assert(var->negatedvar->negatedvar == var);
10448 SCIP_CALL( SCIPvarAddVub(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
10449 branchcand, eventqueue, vlbvar, -vlbcoef, var->data.negate.constant - vlbconstant, transitive, infeasible,
10450 nbdchgs) );
10451 break;
10452
10453 default:
10454 SCIPerrorMessage("unknown variable status\n");
10455 return SCIP_INVALIDDATA;
10456 }
10457
10458 return SCIP_OKAY;
10459 }
10460
10461 /** informs variable x about a globally valid variable upper bound x <= b*z + d with integer variable z;
10462 * if z is binary, the corresponding valid implication for z is also added;
10463 * updates the global bounds of the variable and the vub variable correspondingly
10464 */
10465 SCIP_RETCODE SCIPvarAddVub(
10466 SCIP_VAR* var, /**< problem variable */
10467 BMS_BLKMEM* blkmem, /**< block memory */
10468 SCIP_SET* set, /**< global SCIP settings */
10469 SCIP_STAT* stat, /**< problem statistics */
10470 SCIP_PROB* transprob, /**< transformed problem */
10471 SCIP_PROB* origprob, /**< original problem */
10472 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
10473 SCIP_REOPT* reopt, /**< reoptimization data structure */
10474 SCIP_LP* lp, /**< current LP data */
10475 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
10476 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
10477 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
10478 SCIP_VAR* vubvar, /**< variable z in x <= b*z + d */
10479 SCIP_Real vubcoef, /**< coefficient b in x <= b*z + d */
10480 SCIP_Real vubconstant, /**< constant d in x <= b*z + d */
10481 SCIP_Bool transitive, /**< should transitive closure of implication also be added? */
10482 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
10483 int* nbdchgs /**< pointer to store the number of performed bound changes, or NULL */
10484 )
10485 {
10486 assert(var != NULL);
10487 assert(set != NULL);
10488 assert(var->scip == set->scip);
10489 assert(SCIPvarGetType(vubvar) != SCIP_VARTYPE_CONTINUOUS);
10490 assert(infeasible != NULL);
10491
10492 SCIPsetDebugMsg(set, "adding variable upper bound <%s> <= %g<%s> + %g\n", SCIPvarGetName(var), vubcoef, SCIPvarGetName(vubvar), vubconstant);
10493
10494 *infeasible = FALSE;
10495 if( nbdchgs != NULL )
10496 *nbdchgs = 0;
10497
10498 switch( SCIPvarGetStatus(var) )
10499 {
10500 case SCIP_VARSTATUS_ORIGINAL:
10501 assert(var->data.original.transvar != NULL);
10502 SCIP_CALL( SCIPvarAddVub(var->data.original.transvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10503 cliquetable, branchcand, eventqueue, vubvar, vubcoef, vubconstant, transitive, infeasible, nbdchgs) );
10504 break;
10505
10506 case SCIP_VARSTATUS_COLUMN:
10507 case SCIP_VARSTATUS_LOOSE:
10508 case SCIP_VARSTATUS_FIXED:
10509 /* transform b*z + d into the corresponding sum after transforming z to an active problem variable */
10510 SCIP_CALL( SCIPvarGetProbvarSum(&vubvar, set, &vubcoef, &vubconstant) );
10511 SCIPsetDebugMsg(set, " -> transformed to variable upper bound <%s> <= %g<%s> + %g\n",
10512 SCIPvarGetName(var), vubcoef, SCIPvarGetName(vubvar), vubconstant);
10513
10514 /* if the vub coefficient is zero, just update the upper bound of the variable */
10515 if( SCIPsetIsZero(set, vubcoef) )
10516 {
10517 if( SCIPsetIsFeasLT(set, vubconstant, SCIPvarGetLbGlobal(var)) )
10518 *infeasible = TRUE;
10519 else if( SCIPsetIsFeasLT(set, vubconstant, SCIPvarGetUbGlobal(var)) )
10520 {
10521 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10522 * with the local bound, in this case we need to store the bound change as pending bound change
10523 */
10524 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10525 {
10526 assert(tree != NULL);
10527 assert(transprob != NULL);
10528 assert(SCIPprobIsTransformed(transprob));
10529
10530 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10531 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, vubconstant, SCIP_BOUNDTYPE_UPPER, FALSE) );
10532 }
10533 else
10534 {
10535 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, vubconstant) );
10536 }
10537
10538 if( nbdchgs != NULL )
10539 (*nbdchgs)++;
10540 }
10541 }
10542 else if( var == vubvar )
10543 {
10544 /* the variables cancels out, the variable bound constraint is either redundant or proves global infeasibility */
10545 if( SCIPsetIsEQ(set, vubcoef, 1.0) )
10546 {
10547 if( SCIPsetIsNegative(set, vubconstant) )
10548 *infeasible = TRUE;
10549 return SCIP_OKAY;
10550 }
10551 else
10552 {
10553 SCIP_Real lb = SCIPvarGetLbGlobal(var);
10554 SCIP_Real ub = SCIPvarGetUbGlobal(var);
10555
10556 /* the variable bound constraint defines a new lower bound */
10557 if( SCIPsetIsGT(set, vubcoef, 1.0) )
10558 {
10559 SCIP_Real newlb = vubconstant / (1.0 - vubcoef);
10560
10561 if( SCIPsetIsFeasGT(set, newlb, ub) )
10562 {
10563 *infeasible = TRUE;
10564 return SCIP_OKAY;
10565 }
10566 else if( SCIPsetIsFeasGT(set, newlb, lb) )
10567 {
10568 /* bound might be adjusted due to integrality condition */
10569 newlb = adjustedLb(set, SCIPvarGetType(var), newlb);
10570
10571 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10572 * with the local bound, in this case we need to store the bound change as pending bound change
10573 */
10574 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10575 {
10576 assert(tree != NULL);
10577 assert(transprob != NULL);
10578 assert(SCIPprobIsTransformed(transprob));
10579
10580 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10581 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, newlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
10582 }
10583 else
10584 {
10585 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newlb) );
10586 }
10587
10588 if( nbdchgs != NULL )
10589 (*nbdchgs)++;
10590 }
10591 }
10592 /* the variable bound constraint defines a new upper bound */
10593 else
10594 {
10595 SCIP_Real newub;
10596
10597 assert(SCIPsetIsLT(set, vubcoef, 1.0));
10598
10599 newub = vubconstant / (1.0 - vubcoef);
10600
10601 if( SCIPsetIsFeasLT(set, newub, lb) )
10602 {
10603 *infeasible = TRUE;
10604 return SCIP_OKAY;
10605 }
10606 else if( SCIPsetIsFeasLT(set, newub, ub) )
10607 {
10608 /* bound might be adjusted due to integrality condition */
10609 newub = adjustedUb(set, SCIPvarGetType(var), newub);
10610
10611 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10612 * with the local bound, in this case we need to store the bound change as pending bound change
10613 */
10614 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10615 {
10616 assert(tree != NULL);
10617 assert(transprob != NULL);
10618 assert(SCIPprobIsTransformed(transprob));
10619
10620 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10621 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, newub, SCIP_BOUNDTYPE_UPPER, FALSE) );
10622 }
10623 else
10624 {
10625 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newub) );
10626 }
10627
10628 if( nbdchgs != NULL )
10629 (*nbdchgs)++;
10630 }
10631 }
10632 }
10633 }
10634 else if( SCIPvarIsActive(vubvar) )
10635 {
10636 SCIP_Real xlb;
10637 SCIP_Real xub;
10638 SCIP_Real zlb;
10639 SCIP_Real zub;
10640 SCIP_Real minvub;
10641 SCIP_Real maxvub;
10642
10643 assert(SCIPvarGetStatus(vubvar) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(vubvar) == SCIP_VARSTATUS_COLUMN);
10644 assert(vubcoef != 0.0);
10645
10646 minvub = -SCIPsetInfinity(set);
10647 maxvub = SCIPsetInfinity(set);
10648
10649 xlb = SCIPvarGetLbGlobal(var);
10650 xub = SCIPvarGetUbGlobal(var);
10651 zlb = SCIPvarGetLbGlobal(vubvar);
10652 zub = SCIPvarGetUbGlobal(vubvar);
10653
10654 /* improve global bounds of vub variable, and calculate minimal and maximal value of variable bound */
10655 if( vubcoef >= 0.0 )
10656 {
10657 SCIP_Real newzlb;
10658
10659 if( !SCIPsetIsInfinity(set, -xlb) )
10660 {
10661 /* x <= b*z + d -> z >= (x-d)/b */
10662 newzlb = (xlb - vubconstant)/vubcoef;
10663 if( SCIPsetIsFeasGT(set, newzlb, zub) )
10664 {
10665 *infeasible = TRUE;
10666 return SCIP_OKAY;
10667 }
10668 if( SCIPsetIsFeasGT(set, newzlb, zlb) )
10669 {
10670 /* bound might be adjusted due to integrality condition */
10671 newzlb = adjustedLb(set, SCIPvarGetType(vubvar), newzlb);
10672
10673 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10674 * with the local bound, in this case we need to store the bound change as pending bound change
10675 */
10676 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10677 {
10678 assert(tree != NULL);
10679 assert(transprob != NULL);
10680 assert(SCIPprobIsTransformed(transprob));
10681
10682 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10683 tree, reopt, lp, branchcand, eventqueue, cliquetable, vubvar, newzlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
10684 }
10685 else
10686 {
10687 SCIP_CALL( SCIPvarChgLbGlobal(vubvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newzlb) );
10688 }
10689 zlb = newzlb;
10690
10691 if( nbdchgs != NULL )
10692 (*nbdchgs)++;
10693 }
10694 minvub = vubcoef * zlb + vubconstant;
10695 if( !SCIPsetIsInfinity(set, zub) )
10696 maxvub = vubcoef * zub + vubconstant;
10697 }
10698 else
10699 {
10700 if( !SCIPsetIsInfinity(set, zub) )
10701 maxvub = vubcoef * zub + vubconstant;
10702 if( !SCIPsetIsInfinity(set, -zlb) )
10703 minvub = vubcoef * zlb + vubconstant;
10704 }
10705 }
10706 else
10707 {
10708 SCIP_Real newzub;
10709
10710 if( !SCIPsetIsInfinity(set, -xlb) )
10711 {
10712 /* x <= b*z + d -> z <= (x-d)/b */
10713 newzub = (xlb - vubconstant)/vubcoef;
10714 if( SCIPsetIsFeasLT(set, newzub, zlb) )
10715 {
10716 *infeasible = TRUE;
10717 return SCIP_OKAY;
10718 }
10719 if( SCIPsetIsFeasLT(set, newzub, zub) )
10720 {
10721 /* bound might be adjusted due to integrality condition */
10722 newzub = adjustedUb(set, SCIPvarGetType(vubvar), newzub);
10723
10724 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10725 * with the local bound, in this case we need to store the bound change as pending bound change
10726 */
10727 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10728 {
10729 assert(tree != NULL);
10730 assert(transprob != NULL);
10731 assert(SCIPprobIsTransformed(transprob));
10732
10733 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10734 tree, reopt, lp, branchcand, eventqueue, cliquetable, vubvar, newzub, SCIP_BOUNDTYPE_UPPER, FALSE) );
10735 }
10736 else
10737 {
10738 SCIP_CALL( SCIPvarChgUbGlobal(vubvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newzub) );
10739 }
10740 zub = newzub;
10741
10742 if( nbdchgs != NULL )
10743 (*nbdchgs)++;
10744 }
10745 minvub = vubcoef * zub + vubconstant;
10746 if( !SCIPsetIsInfinity(set, -zlb) )
10747 maxvub = vubcoef * zlb + vubconstant;
10748 }
10749 else
10750 {
10751 if( !SCIPsetIsInfinity(set, zub) )
10752 minvub = vubcoef * zub + vubconstant;
10753 if( !SCIPsetIsInfinity(set, -zlb) )
10754 maxvub = vubcoef * zlb + vubconstant;
10755 }
10756 }
10757 if( minvub > maxvub )
10758 minvub = maxvub;
10759
10760 /* adjust bounds due to integrality of vub variable */
10761 minvub = adjustedUb(set, SCIPvarGetType(var), minvub);
10762 maxvub = adjustedUb(set, SCIPvarGetType(var), maxvub);
10763
10764 /* check bounds for feasibility */
10765 if( SCIPsetIsFeasLT(set, maxvub, xlb) || (var == vubvar && SCIPsetIsEQ(set, vubcoef, 1.0) && SCIPsetIsFeasNegative(set, vubconstant)) )
10766 {
10767 *infeasible = TRUE;
10768 return SCIP_OKAY;
10769 }
10770
10771 /* improve global upper bound of variable */
10772 if( SCIPsetIsFeasLT(set, maxvub, xub) )
10773 {
10774 /* bound might be adjusted due to integrality condition */
10775 maxvub = adjustedUb(set, SCIPvarGetType(var), maxvub);
10776
10777 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10778 * with the local bound, in this case we need to store the bound change as pending bound change
10779 */
10780 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10781 {
10782 assert(tree != NULL);
10783 assert(transprob != NULL);
10784 assert(SCIPprobIsTransformed(transprob));
10785
10786 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10787 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, maxvub, SCIP_BOUNDTYPE_UPPER, FALSE) );
10788 }
10789 else
10790 {
10791 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, maxvub) );
10792 }
10793 xub = maxvub;
10794
10795 if( nbdchgs != NULL )
10796 (*nbdchgs)++;
10797 }
10798 maxvub = xub;
10799
10800 /* improve variable bound for binary z by moving the variable's global bound to the vub constant */
10801 if( SCIPvarIsBinary(vubvar) )
10802 {
10803 /* b > 0: x <= (maxvub - minvub) * z + minvub
10804 * b < 0: x <= (minvub - maxvub) * z + maxvub
10805 */
10806
10807 assert(!SCIPsetIsInfinity(set, maxvub) && !SCIPsetIsInfinity(set, -minvub));
10808
10809 if( vubcoef >= 0.0 )
10810 {
10811 vubcoef = maxvub - minvub;
10812 vubconstant = minvub;
10813 }
10814 else
10815 {
10816 vubcoef = minvub - maxvub;
10817 vubconstant = maxvub;
10818 }
10819 }
10820
10821 /* add variable bound to the variable bounds list */
10822 if( SCIPsetIsFeasLT(set, minvub, xub) )
10823 {
10824 assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_FIXED);
10825 assert(!SCIPsetIsZero(set, vubcoef));
10826
10827 /* if one of the variables is binary, add the corresponding implication to the variable's implication
10828 * list, thereby also adding the variable bound (or implication) to the other variable
10829 */
10830 if( SCIPvarGetType(vubvar) == SCIP_VARTYPE_BINARY )
10831 {
10832 /* add corresponding implication:
10833 * b > 0, x <= b*z + d <-> z == 0 -> x <= d
10834 * b < 0, x <= b*z + d <-> z == 1 -> x <= b+d
10835 */
10836 SCIP_CALL( varAddTransitiveImplic(vubvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10837 cliquetable, branchcand, eventqueue, (vubcoef < 0.0), var, SCIP_BOUNDTYPE_UPPER, minvub, transitive,
10838 infeasible, nbdchgs) );
10839 }
10840 else if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY )
10841 {
10842 /* add corresponding implication:
10843 * b > 0, x <= b*z + d <-> x == 1 -> z >= (1-d)/b
10844 * b < 0, x <= b*z + d <-> x == 1 -> z <= (1-d)/b
10845 */
10846 SCIP_CALL( varAddTransitiveImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10847 cliquetable, branchcand, eventqueue, TRUE, vubvar, (vubcoef >= 0.0 ? SCIP_BOUNDTYPE_LOWER : SCIP_BOUNDTYPE_UPPER),
10848 (1.0-vubconstant)/vubcoef, transitive, infeasible, nbdchgs) );
10849 }
10850 else
10851 {
10852 SCIP_CALL( varAddVbound(var, blkmem, set, eventqueue, SCIP_BOUNDTYPE_UPPER, vubvar, vubcoef, vubconstant) );
10853 }
10854 }
10855 }
10856 break;
10857
10858 case SCIP_VARSTATUS_AGGREGATED:
10859 /* x = a*y + c: x <= b*z + d <=> a*y + c <= b*z + d <=> y <= b/a * z + (d-c)/a, if a > 0
10860 * y >= b/a * z + (d-c)/a, if a < 0
10861 */
10862 assert(var->data.aggregate.var != NULL);
10863 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
10864 {
10865 /* a > 0 -> add variable upper bound */
10866 SCIP_CALL( SCIPvarAddVub(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10867 cliquetable, branchcand, eventqueue, vubvar, vubcoef/var->data.aggregate.scalar,
10868 (vubconstant - var->data.aggregate.constant)/var->data.aggregate.scalar, transitive, infeasible, nbdchgs) );
10869 }
10870 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
10871 {
10872 /* a < 0 -> add variable lower bound */
10873 SCIP_CALL( SCIPvarAddVlb(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10874 cliquetable, branchcand, eventqueue, vubvar, vubcoef/var->data.aggregate.scalar,
10875 (vubconstant - var->data.aggregate.constant)/var->data.aggregate.scalar, transitive, infeasible, nbdchgs) );
10876 }
10877 else
10878 {
10879 SCIPerrorMessage("scalar is zero in aggregation\n");
10880 return SCIP_INVALIDDATA;
10881 }
10882 break;
10883
10884 case SCIP_VARSTATUS_MULTAGGR:
10885 /* nothing to do here */
10886 break;
10887
10888 case SCIP_VARSTATUS_NEGATED:
10889 /* x = offset - x': x <= b*z + d <=> offset - x' <= b*z + d <=> x' >= -b*z + (offset-d) */
10890 assert(var->negatedvar != NULL);
10891 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
10892 assert(var->negatedvar->negatedvar == var);
10893 SCIP_CALL( SCIPvarAddVlb(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
10894 branchcand, eventqueue, vubvar, -vubcoef, var->data.negate.constant - vubconstant, transitive, infeasible,
10895 nbdchgs) );
10896 break;
10897
10898 default:
10899 SCIPerrorMessage("unknown variable status\n");
10900 return SCIP_INVALIDDATA;
10901 }
10902
10903 return SCIP_OKAY;
10904 }
10905
10906 /** informs binary variable x about a globally valid implication: x == 0 or x == 1 ==> y <= b or y >= b;
10907 * also adds the corresponding implication or variable bound to the implied variable;
10908 * if the implication is conflicting, the variable is fixed to the opposite value;
10909 * if the variable is already fixed to the given value, the implication is performed immediately;
10910 * if the implication is redundant with respect to the variables' global bounds, it is ignored
10911 */
10912 SCIP_RETCODE SCIPvarAddImplic(
10913 SCIP_VAR* var, /**< problem variable */
10914 BMS_BLKMEM* blkmem, /**< block memory */
10915 SCIP_SET* set, /**< global SCIP settings */
10916 SCIP_STAT* stat, /**< problem statistics */
10917 SCIP_PROB* transprob, /**< transformed problem */
10918 SCIP_PROB* origprob, /**< original problem */
10919 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
10920 SCIP_REOPT* reopt, /**< reoptimization data structure */
10921 SCIP_LP* lp, /**< current LP data */
10922 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
10923 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
10924 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
10925 SCIP_Bool varfixing, /**< FALSE if y should be added in implications for x == 0, TRUE for x == 1 */
10926 SCIP_VAR* implvar, /**< variable y in implication y <= b or y >= b */
10927 SCIP_BOUNDTYPE impltype, /**< type of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
10928 SCIP_Real implbound, /**< bound b in implication y <= b or y >= b */
10929 SCIP_Bool transitive, /**< should transitive closure of implication also be added? */
10930 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
10931 int* nbdchgs /**< pointer to store the number of performed bound changes, or NULL */
10932 )
10933 {
10934 assert(var != NULL);
10935 assert(set != NULL);
10936 assert(var->scip == set->scip);
10937 assert(SCIPvarGetType(var) == SCIP_VARTYPE_BINARY);
10938 assert(infeasible != NULL);
10939
10940 *infeasible = FALSE;
10941 if( nbdchgs != NULL )
10942 *nbdchgs = 0;
10943
10944 switch( SCIPvarGetStatus(var) )
10945 {
10946 case SCIP_VARSTATUS_ORIGINAL:
10947 assert(var->data.original.transvar != NULL);
10948 SCIP_CALL( SCIPvarAddImplic(var->data.original.transvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10949 cliquetable, branchcand, eventqueue, varfixing, implvar, impltype, implbound, transitive, infeasible,
10950 nbdchgs) );
10951 break;
10952
10953 case SCIP_VARSTATUS_COLUMN:
10954 case SCIP_VARSTATUS_LOOSE:
10955 /* if the variable is fixed (although it has no FIXED status), and varfixing corresponds to the fixed value of
10956 * the variable, the implication can be applied directly;
10957 * otherwise, add implication to the implications list (and add inverse of implication to the implied variable)
10958 */
10959 if( SCIPvarGetLbGlobal(var) > 0.5 || SCIPvarGetUbGlobal(var) < 0.5 )
10960 {
10961 if( varfixing == (SCIPvarGetLbGlobal(var) > 0.5) )
10962 {
10963 SCIP_CALL( applyImplic(blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, eventqueue,
10964 cliquetable, implvar, impltype, implbound, infeasible, nbdchgs) );
10965 }
10966 }
10967 else
10968 {
10969 SCIP_CALL( SCIPvarGetProbvarBound(&implvar, &implbound, &impltype) );
10970 SCIPvarAdjustBd(implvar, set, impltype, &implbound);
10971 if( SCIPvarIsActive(implvar) || SCIPvarGetStatus(implvar) == SCIP_VARSTATUS_FIXED )
10972 {
10973 SCIP_CALL( varAddTransitiveImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
10974 branchcand, eventqueue, varfixing, implvar, impltype, implbound, transitive, infeasible, nbdchgs) );
10975 }
10976 }
10977 break;
10978
10979 case SCIP_VARSTATUS_FIXED:
10980 /* if varfixing corresponds to the fixed value of the variable, the implication can be applied directly */
10981 if( varfixing == (SCIPvarGetLbGlobal(var) > 0.5) )
10982 {
10983 SCIP_CALL( applyImplic(blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, eventqueue,
10984 cliquetable, implvar, impltype, implbound, infeasible, nbdchgs) );
10985 }
10986 break;
10987
10988 case SCIP_VARSTATUS_AGGREGATED:
10989 /* implication added for x == 1:
10990 * x == 1 && x = 1*z + 0 ==> y <= b or y >= b <==> z >= 1 ==> y <= b or y >= b
10991 * x == 1 && x = -1*z + 1 ==> y <= b or y >= b <==> z <= 0 ==> y <= b or y >= b
10992 * implication added for x == 0:
10993 * x == 0 && x = 1*z + 0 ==> y <= b or y >= b <==> z <= 0 ==> y <= b or y >= b
10994 * x == 0 && x = -1*z + 1 ==> y <= b or y >= b <==> z >= 1 ==> y <= b or y >= b
10995 *
10996 * use only binary variables z
10997 */
10998 assert(var->data.aggregate.var != NULL);
10999 if( SCIPvarIsBinary(var->data.aggregate.var) )
11000 {
11001 assert( (SCIPsetIsEQ(set, var->data.aggregate.scalar, 1.0) && SCIPsetIsZero(set, var->data.aggregate.constant))
11002 || (SCIPsetIsEQ(set, var->data.aggregate.scalar, -1.0) && SCIPsetIsEQ(set, var->data.aggregate.constant, 1.0)) );
11003
11004 if( var->data.aggregate.scalar > 0 )
11005 {
11006 SCIP_CALL( SCIPvarAddImplic(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
11007 cliquetable, branchcand, eventqueue, varfixing, implvar, impltype, implbound, transitive, infeasible,
11008 nbdchgs) );
11009 }
11010 else
11011 {
11012 SCIP_CALL( SCIPvarAddImplic(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
11013 cliquetable, branchcand, eventqueue, !varfixing, implvar, impltype, implbound, transitive, infeasible,
11014 nbdchgs) );
11015 }
11016 }
11017 break;
11018
11019 case SCIP_VARSTATUS_MULTAGGR:
11020 /* nothing to do here */
11021 break;
11022
11023 case SCIP_VARSTATUS_NEGATED:
11024 /* implication added for x == 1:
11025 * x == 1 && x = -1*z + 1 ==> y <= b or y >= b <==> z <= 0 ==> y <= b or y >= b
11026 * implication added for x == 0:
11027 * x == 0 && x = -1*z + 1 ==> y <= b or y >= b <==> z >= 1 ==> y <= b or y >= b
11028 */
11029 assert(var->negatedvar != NULL);
11030 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
11031 assert(var->negatedvar->negatedvar == var);
11032 assert(SCIPvarIsBinary(var->negatedvar));
11033
11034 if( SCIPvarGetType(var->negatedvar) == SCIP_VARTYPE_BINARY )
11035 {
11036 SCIP_CALL( SCIPvarAddImplic(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
11037 cliquetable, branchcand, eventqueue, !varfixing, implvar, impltype, implbound, transitive, infeasible, nbdchgs) );
11038 }
11039 /* in case one both variables are not of binary type we have to add the implication as variable bounds */
11040 else
11041 {
11042 /* if the implied variable is of binary type exchange the variables */
11043 if( SCIPvarGetType(implvar) == SCIP_VARTYPE_BINARY )
11044 {
11045 SCIP_CALL( SCIPvarAddImplic(implvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
11046 branchcand, eventqueue, (impltype == SCIP_BOUNDTYPE_UPPER) ? TRUE : FALSE, var->negatedvar,
11047 varfixing ? SCIP_BOUNDTYPE_LOWER : SCIP_BOUNDTYPE_UPPER, varfixing ? 1.0 : 0.0, transitive,
11048 infeasible, nbdchgs) );
11049 }
11050 else
11051 {
11052 /* both variables are not of binary type but are implicit binary; in that case we can only add this
11053 * implication as variable bounds
11054 */
11055
11056 /* add variable lower bound on the negation of var */
11057 if( varfixing )
11058 {
11059 /* (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
11060 * as variable lower bound
11061 */
11062 SCIP_CALL( SCIPvarAddVlb(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
11063 cliquetable, branchcand, eventqueue, implvar, (impltype == SCIP_BOUNDTYPE_UPPER) ? 1.0 : -1.0,
11064 (impltype == SCIP_BOUNDTYPE_UPPER) ? 0.0 : 1.0, transitive, infeasible, nbdchgs) );
11065 }
11066 else
11067 {
11068 /* (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
11069 * as variable upper bound
11070 */
11071 SCIP_CALL( SCIPvarAddVub(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
11072 cliquetable, branchcand, eventqueue, implvar, (impltype == SCIP_BOUNDTYPE_UPPER) ? -1.0 : 1.0,
11073 (impltype == SCIP_BOUNDTYPE_UPPER) ? 1.0 : 0.0, transitive, infeasible, nbdchgs) );
11074 }
11075
11076 /* add variable bound on implvar */
11077 if( impltype == SCIP_BOUNDTYPE_UPPER )
11078 {
11079 /* (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
11080 * as variable upper bound
11081 */
11082 SCIP_CALL( SCIPvarAddVub(implvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
11083 branchcand, eventqueue, var->negatedvar, (varfixing) ? 1.0 : -1.0,
11084 (varfixing) ? 0.0 : 1.0, transitive, infeasible, nbdchgs) );
11085 }
11086 else
11087 {
11088 /* (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
11089 * as variable upper bound
11090 */
11091 SCIP_CALL( SCIPvarAddVlb(implvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
11092 branchcand, eventqueue, var->negatedvar, (varfixing) ? -1.0 : 1.0, (varfixing) ? 1.0 : 0.0,
11093 transitive, infeasible, nbdchgs) );
11094 }
11095 }
11096 }
11097 break;
11098
11099 default:
11100 SCIPerrorMessage("unknown variable status\n");
11101 return SCIP_INVALIDDATA;
11102 }
11103
11104 return SCIP_OKAY;
11105 }
11106
11107 /** returns whether there is an implication x == varfixing -> y <= b or y >= b in the implication graph;
11108 * implications that are represented as cliques in the clique table are not regarded (use SCIPvarsHaveCommonClique());
11109 * both variables must be active, variable x must be binary
11110 */
11111 SCIP_Bool SCIPvarHasImplic(
11112 SCIP_VAR* var, /**< problem variable x */
11113 SCIP_Bool varfixing, /**< FALSE if y should be searched in implications for x == 0, TRUE for x == 1 */
11114 SCIP_VAR* implvar, /**< variable y to search for */
11115 SCIP_BOUNDTYPE impltype /**< type of implication y <=/>= b to search for */
11116 )
11117 {
11118 assert(var != NULL);
11119 assert(implvar != NULL);
11120 assert(SCIPvarIsActive(var));
11121 assert(SCIPvarIsActive(implvar));
11122 assert(SCIPvarIsBinary(var));
11123
11124 return var->implics != NULL && SCIPimplicsContainsImpl(var->implics, varfixing, implvar, impltype);
11125 }
11126
11127 /** returns whether there is an implication x == varfixing -> y == implvarfixing in the implication graph;
11128 * implications that are represented as cliques in the clique table are not regarded (use SCIPvarsHaveCommonClique());
11129 * both variables must be active binary variables
11130 */
11131 SCIP_Bool SCIPvarHasBinaryImplic(
11132 SCIP_VAR* var, /**< problem variable x */
11133 SCIP_Bool varfixing, /**< FALSE if y should be searched in implications for x == 0, TRUE for x == 1 */
11134 SCIP_VAR* implvar, /**< variable y to search for */
11135 SCIP_Bool implvarfixing /**< value of the implied variable to search for */
11136 )
11137 {
11138 assert(SCIPvarIsBinary(implvar));
11139
11140 return SCIPvarHasImplic(var, varfixing, implvar, implvarfixing ? SCIP_BOUNDTYPE_LOWER : SCIP_BOUNDTYPE_UPPER);
11141 }
11142
11143 /** gets the values of b in implications x == varfixing -> y <= b or y >= b in the implication graph;
11144 * the values are set to SCIP_INVALID if there is no implied bound
11145 */
11146 void SCIPvarGetImplicVarBounds(
11147 SCIP_VAR* var, /**< problem variable x */
11148 SCIP_Bool varfixing, /**< FALSE if y should be searched in implications for x == 0, TRUE for x == 1 */
11149 SCIP_VAR* implvar, /**< variable y to search for */
11150 SCIP_Real* lb, /**< buffer to store the value of the implied lower bound */
11151 SCIP_Real* ub /**< buffer to store the value of the implied upper bound */
11152 )
11153 {
11154 int lowerpos;
11155 int upperpos;
11156 SCIP_Real* bounds;
11157
11158 assert(lb != NULL);
11159 assert(ub != NULL);
11160
11161 *lb = SCIP_INVALID;
11162 *ub = SCIP_INVALID;
11163
11164 if( var->implics == NULL )
11165 return;
11166
11167 SCIPimplicsGetVarImplicPoss(var->implics, varfixing, implvar, &lowerpos, &upperpos);
11168 bounds = SCIPvarGetImplBounds(var, varfixing);
11169
11170 if( bounds == NULL )
11171 return;
11172
11173 if( lowerpos >= 0 )
11174 *lb = bounds[lowerpos];
11175
11176 if( upperpos >= 0 )
11177 *ub = bounds[upperpos];
11178 }
11179
11180
11181 /** fixes the bounds of a binary variable to the given value, counting bound changes and detecting infeasibility */
11182 SCIP_RETCODE SCIPvarFixBinary(
11183 SCIP_VAR* var, /**< problem variable */
11184 BMS_BLKMEM* blkmem, /**< block memory */
11185 SCIP_SET* set, /**< global SCIP settings */
11186 SCIP_STAT* stat, /**< problem statistics */
11187 SCIP_PROB* transprob, /**< transformed problem */
11188 SCIP_PROB* origprob, /**< original problem */
11189 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
11190 SCIP_REOPT* reopt, /**< reoptimization data structure */
11191 SCIP_LP* lp, /**< current LP data */
11192 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
11193 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
11194 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
11195 SCIP_Bool value, /**< value to fix variable to */
11196 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
11197 int* nbdchgs /**< pointer to count the number of performed bound changes, or NULL */
11198 )
11199 {
11200 assert(var != NULL);
11201 assert(set != NULL);
11202 assert(var->scip == set->scip);
11203 assert(infeasible != NULL);
11204
11205 *infeasible = FALSE;
11206
11207 if( value == FALSE )
11208 {
11209 if( var->glbdom.lb > 0.5 )
11210 *infeasible = TRUE;
11211 else if( var->glbdom.ub > 0.5 )
11212 {
11213 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
11214 * with the local bound, in this case we need to store the bound change as pending bound change
11215 */
11216 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
11217 {
11218 assert(tree != NULL);
11219 assert(transprob != NULL);
11220 assert(SCIPprobIsTransformed(transprob));
11221
11222 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
11223 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, 0.0, SCIP_BOUNDTYPE_UPPER, FALSE) );
11224 }
11225 else
11226 {
11227 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, 0.0) );
11228 }
11229
11230 if( nbdchgs != NULL )
11231 (*nbdchgs)++;
11232 }
11233 }
11234 else
11235 {
11236 if( var->glbdom.ub < 0.5 )
11237 *infeasible = TRUE;
11238 else if( var->glbdom.lb < 0.5 )
11239 {
11240 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
11241 * with the local bound, in this case we need to store the bound change as pending bound change
11242 */
11243 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
11244 {
11245 assert(tree != NULL);
11246 assert(transprob != NULL);
11247 assert(SCIPprobIsTransformed(transprob));
11248
11249 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
11250 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, 1.0, SCIP_BOUNDTYPE_LOWER, FALSE) );
11251 }
11252 else
11253 {
11254 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, 1.0) );
11255 }
11256
11257 if( nbdchgs != NULL )
11258 (*nbdchgs)++;
11259 }
11260 }
11261
11262 return SCIP_OKAY;
11263 }
11264
11265 /** adds the variable to the given clique and updates the list of cliques the binary variable is member of;
11266 * if the variable now appears twice in the clique with the same value, it is fixed to the opposite value;
11267 * if the variable now appears twice in the clique with opposite values, all other variables are fixed to
11268 * the opposite of the value they take in the clique
11269 */
11270 SCIP_RETCODE SCIPvarAddClique(
11271 SCIP_VAR* var, /**< problem variable */
11272 BMS_BLKMEM* blkmem, /**< block memory */
11273 SCIP_SET* set, /**< global SCIP settings */
11274 SCIP_STAT* stat, /**< problem statistics */
11275 SCIP_PROB* transprob, /**< transformed problem */
11276 SCIP_PROB* origprob, /**< original problem */
11277 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
11278 SCIP_REOPT* reopt, /**< reoptimization data structure */
11279 SCIP_LP* lp, /**< current LP data */
11280 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
11281 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
11282 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
11283 SCIP_Bool value, /**< value of the variable in the clique */
11284 SCIP_CLIQUE* clique, /**< clique the variable should be added to */
11285 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
11286 int* nbdchgs /**< pointer to count the number of performed bound changes, or NULL */
11287 )
11288 {
11289 assert(var != NULL);
11290 assert(set != NULL);
11291 assert(var->scip == set->scip);
11292 assert(SCIPvarIsBinary(var));
11293 assert(infeasible != NULL);
11294
11295 *infeasible = FALSE;
11296
11297 /* get corresponding active problem variable */
11298 SCIP_CALL( SCIPvarGetProbvarBinary(&var, &value) );
11299 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
11300 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
11301 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED
11302 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
11303 assert(SCIPvarIsBinary(var));
11304
11305 /* only column and loose variables may be member of a clique */
11306 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
11307 {
11308 SCIP_Bool doubleentry;
11309 SCIP_Bool oppositeentry;
11310
11311 /* add variable to clique */
11312 SCIP_CALL( SCIPcliqueAddVar(clique, blkmem, set, var, value, &doubleentry, &oppositeentry) );
11313
11314 /* add clique to variable's clique list */
11315 SCIP_CALL( SCIPcliquelistAdd(&var->cliquelist, blkmem, set, value, clique) );
11316
11317 /* check consistency of cliquelist */
11318 SCIPcliquelistCheck(var->cliquelist, var);
11319
11320 /* if the variable now appears twice with the same value in the clique, it can be fixed to the opposite value */
11321 if( doubleentry )
11322 {
11323 SCIP_CALL( SCIPvarFixBinary(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand,
11324 eventqueue, cliquetable, !value, infeasible, nbdchgs) );
11325 }
11326
11327 /* if the variable appears with both values in the clique, all other variables of the clique can be fixed
11328 * to the opposite of the value they take in the clique
11329 */
11330 if( oppositeentry )
11331 {
11332 SCIP_VAR** vars;
11333 SCIP_Bool* values;
11334 int nvars;
11335 int i;
11336
11337 nvars = SCIPcliqueGetNVars(clique);
11338 vars = SCIPcliqueGetVars(clique);
11339 values = SCIPcliqueGetValues(clique);
11340 for( i = 0; i < nvars && !(*infeasible); ++i )
11341 {
11342 if( vars[i] == var )
11343 continue;
11344
11345 SCIP_CALL( SCIPvarFixBinary(vars[i], blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand,
11346 eventqueue, cliquetable, !values[i], infeasible, nbdchgs) );
11347 }
11348 }
11349 }
11350
11351 return SCIP_OKAY;
11352 }
11353
11354 /** adds a filled clique to the cliquelists of all corresponding variables */
11355 SCIP_RETCODE SCIPvarsAddClique(
11356 SCIP_VAR** vars, /**< problem variables */
11357 SCIP_Bool* values, /**< values of the variables in the clique */
11358 int nvars, /**< number of problem variables */
11359 BMS_BLKMEM* blkmem, /**< block memory */
11360 SCIP_SET* set, /**< global SCIP settings */
11361 SCIP_CLIQUE* clique /**< clique that contains all given variables and values */
11362 )
11363 {
11364 SCIP_VAR* var;
11365 int v;
11366
11367 assert(vars != NULL);
11368 assert(values != NULL);
11369 assert(nvars > 0);
11370 assert(set != NULL);
11371 assert(blkmem != NULL);
11372 assert(clique != NULL);
11373
11374 for( v = nvars - 1; v >= 0; --v )
11375 {
11376 var = vars[v];
11377 assert(SCIPvarIsBinary(var));
11378 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
11379
11380 /* add clique to variable's clique list */
11381 SCIP_CALL( SCIPcliquelistAdd(&var->cliquelist, blkmem, set, values[v], clique) );
11382
11383 /* check consistency of cliquelist */
11384 SCIPcliquelistCheck(var->cliquelist, var);
11385 }
11386
11387 return SCIP_OKAY;
11388 }
11389
11390 /** adds a clique to the list of cliques of the given binary variable, but does not change the clique
11391 * itself
11392 */
11393 SCIP_RETCODE SCIPvarAddCliqueToList(
11394 SCIP_VAR* var, /**< problem variable */
11395 BMS_BLKMEM* blkmem, /**< block memory */
11396 SCIP_SET* set, /**< global SCIP settings */
11397 SCIP_Bool value, /**< value of the variable in the clique */
11398 SCIP_CLIQUE* clique /**< clique that should be removed from the variable's clique list */
11399 )
11400 {
11401 assert(var != NULL);
11402 assert(SCIPvarIsBinary(var));
11403 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
11404
11405 /* add clique to variable's clique list */
11406 SCIP_CALL( SCIPcliquelistAdd(&var->cliquelist, blkmem, set, value, clique) );
11407
11408 return SCIP_OKAY;
11409 }
11410
11411
11412 /** deletes a clique from the list of cliques the binary variable is member of, but does not change the clique
11413 * itself
11414 */
11415 SCIP_RETCODE SCIPvarDelCliqueFromList(
11416 SCIP_VAR* var, /**< problem variable */
11417 BMS_BLKMEM* blkmem, /**< block memory */
11418 SCIP_Bool value, /**< value of the variable in the clique */
11419 SCIP_CLIQUE* clique /**< clique that should be removed from the variable's clique list */
11420 )
11421 {
11422 assert(var != NULL);
11423 assert(SCIPvarIsBinary(var));
11424
11425 /* delete clique from variable's clique list */
11426 SCIP_CALL( SCIPcliquelistDel(&var->cliquelist, blkmem, value, clique) );
11427
11428 return SCIP_OKAY;
11429 }
11430
11431 /** deletes the variable from the given clique and updates the list of cliques the binary variable is member of */
11432 SCIP_RETCODE SCIPvarDelClique(
11433 SCIP_VAR* var, /**< problem variable */
11434 BMS_BLKMEM* blkmem, /**< block memory */
11435 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
11436 SCIP_Bool value, /**< value of the variable in the clique */
11437 SCIP_CLIQUE* clique /**< clique the variable should be removed from */
11438 )
11439 {
11440 assert(var != NULL);
11441 assert(SCIPvarIsBinary(var));
11442
11443 /* get corresponding active problem variable */
11444 SCIP_CALL( SCIPvarGetProbvarBinary(&var, &value) );
11445 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
11446 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
11447 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED
11448 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
11449 assert(SCIPvarIsBinary(var));
11450
11451 /* only column and loose variables may be member of a clique */
11452 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
11453 {
11454 /* delete clique from variable's clique list */
11455 SCIP_CALL( SCIPcliquelistDel(&var->cliquelist, blkmem, value, clique) );
11456
11457 /* delete variable from clique */
11458 SCIPcliqueDelVar(clique, cliquetable, var, value);
11459
11460 /* check consistency of cliquelist */
11461 SCIPcliquelistCheck(var->cliquelist, var);
11462 }
11463
11464 return SCIP_OKAY;
11465 }
11466
11467 /** returns whether there is a clique that contains both given variable/value pairs;
11468 * the variables must be active binary variables;
11469 * if regardimplics is FALSE, only the cliques in the clique table are looked at;
11470 * if regardimplics is TRUE, both the cliques and the implications of the implication graph are regarded
11471 *
11472 * @note a variable with it's negated variable are NOT! in a clique
11473 * @note a variable with itself are in a clique
11474 */
11475 SCIP_Bool SCIPvarsHaveCommonClique(
11476 SCIP_VAR* var1, /**< first variable */
11477 SCIP_Bool value1, /**< value of first variable */
11478 SCIP_VAR* var2, /**< second variable */
11479 SCIP_Bool value2, /**< value of second variable */
11480 SCIP_Bool regardimplics /**< should the implication graph also be searched for a clique? */
11481 )
11482 {
11483 assert(var1 != NULL);
11484 assert(var2 != NULL);
11485 assert(SCIPvarIsActive(var1));
11486 assert(SCIPvarIsActive(var2));
11487 assert(SCIPvarIsBinary(var1));
11488 assert(SCIPvarIsBinary(var2));
11489
11490 return (SCIPcliquelistsHaveCommonClique(var1->cliquelist, value1, var2->cliquelist, value2)
11491 || (regardimplics && SCIPvarHasImplic(var1, value1, var2, value2 ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER)));
11492 }
11493
11494 /** actually changes the branch factor of the variable and of all parent variables */
11495 static
11496 SCIP_RETCODE varProcessChgBranchFactor(
11497 SCIP_VAR* var, /**< problem variable */
11498 SCIP_SET* set, /**< global SCIP settings */
11499 SCIP_Real branchfactor /**< factor to weigh variable's branching score with */
11500 )
11501 {
11502 SCIP_VAR* parentvar;
11503 SCIP_Real eps;
11504 int i;
11505
11506 assert(var != NULL);
11507 assert(set != NULL);
11508 assert(var->scip == set->scip);
11509
11510 /* only use positive values */
11511 eps = SCIPsetEpsilon(set);
11512 branchfactor = MAX(branchfactor, eps);
11513
11514 SCIPsetDebugMsg(set, "process changing branch factor of <%s> from %f to %f\n", var->name, var->branchfactor, branchfactor);
11515
11516 if( SCIPsetIsEQ(set, branchfactor, var->branchfactor) )
11517 return SCIP_OKAY;
11518
11519 /* change the branch factor */
11520 var->branchfactor = branchfactor;
11521
11522 /* process parent variables */
11523 for( i = 0; i < var->nparentvars; ++i )
11524 {
11525 parentvar = var->parentvars[i];
11526 assert(parentvar != NULL);
11527
11528 switch( SCIPvarGetStatus(parentvar) )
11529 {
11530 case SCIP_VARSTATUS_ORIGINAL:
11531 /* do not change priorities across the border between transformed and original problem */
11532 break;
11533
11534 case SCIP_VARSTATUS_COLUMN:
11535 case SCIP_VARSTATUS_LOOSE:
11536 case SCIP_VARSTATUS_FIXED:
11537 case SCIP_VARSTATUS_MULTAGGR:
11538 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
11539 SCIPABORT();
11540 return SCIP_INVALIDDATA; /*lint !e527*/
11541
11542 case SCIP_VARSTATUS_AGGREGATED:
11543 case SCIP_VARSTATUS_NEGATED:
11544 SCIP_CALL( varProcessChgBranchFactor(parentvar, set, branchfactor) );
11545 break;
11546
11547 default:
11548 SCIPerrorMessage("unknown variable status\n");
11549 SCIPABORT();
11550 return SCIP_ERROR; /*lint !e527*/
11551 }
11552 }
11553
11554 return SCIP_OKAY;
11555 }
11556
11557 /** sets the branch factor of the variable; this value can be used in the branching methods to scale the score
11558 * values of the variables; higher factor leads to a higher probability that this variable is chosen for branching
11559 */
11560 SCIP_RETCODE SCIPvarChgBranchFactor(
11561 SCIP_VAR* var, /**< problem variable */
11562 SCIP_SET* set, /**< global SCIP settings */
11563 SCIP_Real branchfactor /**< factor to weigh variable's branching score with */
11564 )
11565 {
11566 int v;
11567
11568 assert(var != NULL);
11569 assert(set != NULL);
11570 assert(var->scip == set->scip);
11571 assert(branchfactor >= 0.0);
11572
11573 SCIPdebugMessage("changing branch factor of <%s> from %g to %g\n", var->name, var->branchfactor, branchfactor);
11574
11575 if( SCIPsetIsEQ(set, var->branchfactor, branchfactor) )
11576 return SCIP_OKAY;
11577
11578 /* change priorities of attached variables */
11579 switch( SCIPvarGetStatus(var) )
11580 {
11581 case SCIP_VARSTATUS_ORIGINAL:
11582 if( var->data.original.transvar != NULL )
11583 {
11584 SCIP_CALL( SCIPvarChgBranchFactor(var->data.original.transvar, set, branchfactor) );
11585 }
11586 else
11587 {
11588 assert(set->stage == SCIP_STAGE_PROBLEM);
11589 var->branchfactor = branchfactor;
11590 }
11591 break;
11592
11593 case SCIP_VARSTATUS_COLUMN:
11594 case SCIP_VARSTATUS_LOOSE:
11595 case SCIP_VARSTATUS_FIXED:
11596 SCIP_CALL( varProcessChgBranchFactor(var, set, branchfactor) );
11597 break;
11598
11599 case SCIP_VARSTATUS_AGGREGATED:
11600 assert(!var->donotaggr);
11601 assert(var->data.aggregate.var != NULL);
11602 SCIP_CALL( SCIPvarChgBranchFactor(var->data.aggregate.var, set, branchfactor) );
11603 break;
11604
11605 case SCIP_VARSTATUS_MULTAGGR:
11606 assert(!var->donotmultaggr);
11607 for( v = 0; v < var->data.multaggr.nvars; ++v )
11608 {
11609 SCIP_CALL( SCIPvarChgBranchFactor(var->data.multaggr.vars[v], set, branchfactor) );
11610 }
11611 break;
11612
11613 case SCIP_VARSTATUS_NEGATED:
11614 assert(var->negatedvar != NULL);
11615 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
11616 assert(var->negatedvar->negatedvar == var);
11617 SCIP_CALL( SCIPvarChgBranchFactor(var->negatedvar, set, branchfactor) );
11618 break;
11619
11620 default:
11621 SCIPerrorMessage("unknown variable status\n");
11622 SCIPABORT();
11623 return SCIP_ERROR; /*lint !e527*/
11624 }
11625
11626 return SCIP_OKAY;
11627 }
11628
11629 /** actually changes the branch priority of the variable and of all parent variables */
11630 static
11631 SCIP_RETCODE varProcessChgBranchPriority(
11632 SCIP_VAR* var, /**< problem variable */
11633 int branchpriority /**< branching priority of the variable */
11634 )
11635 {
11636 SCIP_VAR* parentvar;
11637 int i;
11638
11639 assert(var != NULL);
11640
11641 SCIPdebugMessage("process changing branch priority of <%s> from %d to %d\n",
11642 var->name, var->branchpriority, branchpriority);
11643
11644 if( branchpriority == var->branchpriority )
11645 return SCIP_OKAY;
11646
11647 /* change the branch priority */
11648 var->branchpriority = branchpriority;
11649
11650 /* process parent variables */
11651 for( i = 0; i < var->nparentvars; ++i )
11652 {
11653 parentvar = var->parentvars[i];
11654 assert(parentvar != NULL);
11655
11656 switch( SCIPvarGetStatus(parentvar) )
11657 {
11658 case SCIP_VARSTATUS_ORIGINAL:
11659 /* do not change priorities across the border between transformed and original problem */
11660 break;
11661
11662 case SCIP_VARSTATUS_COLUMN:
11663 case SCIP_VARSTATUS_LOOSE:
11664 case SCIP_VARSTATUS_FIXED:
11665 case SCIP_VARSTATUS_MULTAGGR:
11666 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
11667 SCIPABORT();
11668 return SCIP_INVALIDDATA; /*lint !e527*/
11669
11670 case SCIP_VARSTATUS_AGGREGATED:
11671 case SCIP_VARSTATUS_NEGATED:
11672 SCIP_CALL( varProcessChgBranchPriority(parentvar, branchpriority) );
11673 break;
11674
11675 default:
11676 SCIPerrorMessage("unknown variable status\n");
11677 return SCIP_ERROR;
11678 }
11679 }
11680
11681 return SCIP_OKAY;
11682 }
11683
11684 /** sets the branch priority of the variable; variables with higher branch priority are always preferred to variables
11685 * with lower priority in selection of branching variable
11686 */
11687 SCIP_RETCODE SCIPvarChgBranchPriority(
11688 SCIP_VAR* var, /**< problem variable */
11689 int branchpriority /**< branching priority of the variable */
11690 )
11691 {
11692 int v;
11693
11694 assert(var != NULL);
11695
11696 SCIPdebugMessage("changing branch priority of <%s> from %d to %d\n", var->name, var->branchpriority, branchpriority);
11697
11698 if( var->branchpriority == branchpriority )
11699 return SCIP_OKAY;
11700
11701 /* change priorities of attached variables */
11702 switch( SCIPvarGetStatus(var) )
11703 {
11704 case SCIP_VARSTATUS_ORIGINAL:
11705 if( var->data.original.transvar != NULL )
11706 {
11707 SCIP_CALL( SCIPvarChgBranchPriority(var->data.original.transvar, branchpriority) );
11708 }
11709 else
11710 var->branchpriority = branchpriority;
11711 break;
11712
11713 case SCIP_VARSTATUS_COLUMN:
11714 case SCIP_VARSTATUS_LOOSE:
11715 case SCIP_VARSTATUS_FIXED:
11716 SCIP_CALL( varProcessChgBranchPriority(var, branchpriority) );
11717 break;
11718
11719 case SCIP_VARSTATUS_AGGREGATED:
11720 assert(!var->donotaggr);
11721 assert(var->data.aggregate.var != NULL);
11722 SCIP_CALL( SCIPvarChgBranchPriority(var->data.aggregate.var, branchpriority) );
11723 break;
11724
11725 case SCIP_VARSTATUS_MULTAGGR:
11726 assert(!var->donotmultaggr);
11727 for( v = 0; v < var->data.multaggr.nvars; ++v )
11728 {
11729 SCIP_CALL( SCIPvarChgBranchPriority(var->data.multaggr.vars[v], branchpriority) );
11730 }
11731 break;
11732
11733 case SCIP_VARSTATUS_NEGATED:
11734 assert(var->negatedvar != NULL);
11735 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
11736 assert(var->negatedvar->negatedvar == var);
11737 SCIP_CALL( SCIPvarChgBranchPriority(var->negatedvar, branchpriority) );
11738 break;
11739
11740 default:
11741 SCIPerrorMessage("unknown variable status\n");
11742 SCIPABORT();
11743 return SCIP_ERROR; /*lint !e527*/
11744 }
11745
11746 return SCIP_OKAY;
11747 }
11748
11749 /** actually changes the branch direction of the variable and of all parent variables */
11750 static
11751 SCIP_RETCODE varProcessChgBranchDirection(
11752 SCIP_VAR* var, /**< problem variable */
11753 SCIP_BRANCHDIR branchdirection /**< preferred branch direction of the variable (downwards, upwards, auto) */
11754 )
11755 {
11756 SCIP_VAR* parentvar;
11757 int i;
11758
11759 assert(var != NULL);
11760
11761 SCIPdebugMessage("process changing branch direction of <%s> from %u to %d\n",
11762 var->name, var->branchdirection, branchdirection);
11763
11764 if( branchdirection == (SCIP_BRANCHDIR)var->branchdirection )
11765 return SCIP_OKAY;
11766
11767 /* change the branch direction */
11768 var->branchdirection = branchdirection; /*lint !e641*/
11769
11770 /* process parent variables */
11771 for( i = 0; i < var->nparentvars; ++i )
11772 {
11773 parentvar = var->parentvars[i];
11774 assert(parentvar != NULL);
11775
11776 switch( SCIPvarGetStatus(parentvar) )
11777 {
11778 case SCIP_VARSTATUS_ORIGINAL:
11779 /* do not change directions across the border between transformed and original problem */
11780 break;
11781
11782 case SCIP_VARSTATUS_COLUMN:
11783 case SCIP_VARSTATUS_LOOSE:
11784 case SCIP_VARSTATUS_FIXED:
11785 case SCIP_VARSTATUS_MULTAGGR:
11786 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
11787 SCIPABORT();
11788 return SCIP_INVALIDDATA; /*lint !e527*/
11789
11790 case SCIP_VARSTATUS_AGGREGATED:
11791 if( parentvar->data.aggregate.scalar > 0.0 )
11792 {
11793 SCIP_CALL( varProcessChgBranchDirection(parentvar, branchdirection) );
11794 }
11795 else
11796 {
11797 SCIP_CALL( varProcessChgBranchDirection(parentvar, SCIPbranchdirOpposite(branchdirection)) );
11798 }
11799 break;
11800
11801 case SCIP_VARSTATUS_NEGATED:
11802 SCIP_CALL( varProcessChgBranchDirection(parentvar, SCIPbranchdirOpposite(branchdirection)) );
11803 break;
11804
11805 default:
11806 SCIPerrorMessage("unknown variable status\n");
11807 SCIPABORT();
11808 return SCIP_ERROR; /*lint !e527*/
11809 }
11810 }
11811
11812 return SCIP_OKAY;
11813 }
11814
11815 /** sets the branch direction of the variable; variables with higher branch direction are always preferred to variables
11816 * with lower direction in selection of branching variable
11817 */
11818 SCIP_RETCODE SCIPvarChgBranchDirection(
11819 SCIP_VAR* var, /**< problem variable */
11820 SCIP_BRANCHDIR branchdirection /**< preferred branch direction of the variable (downwards, upwards, auto) */
11821 )
11822 {
11823 int v;
11824
11825 assert(var != NULL);
11826
11827 SCIPdebugMessage("changing branch direction of <%s> from %u to %d\n", var->name, var->branchdirection, branchdirection);
11828
11829 if( (SCIP_BRANCHDIR)var->branchdirection == branchdirection )
11830 return SCIP_OKAY;
11831
11832 /* change directions of attached variables */
11833 switch( SCIPvarGetStatus(var) )
11834 {
11835 case SCIP_VARSTATUS_ORIGINAL:
11836 if( var->data.original.transvar != NULL )
11837 {
11838 SCIP_CALL( SCIPvarChgBranchDirection(var->data.original.transvar, branchdirection) );
11839 }
11840 else
11841 var->branchdirection = branchdirection; /*lint !e641*/
11842 break;
11843
11844 case SCIP_VARSTATUS_COLUMN:
11845 case SCIP_VARSTATUS_LOOSE:
11846 case SCIP_VARSTATUS_FIXED:
11847 SCIP_CALL( varProcessChgBranchDirection(var, branchdirection) );
11848 break;
11849
11850 case SCIP_VARSTATUS_AGGREGATED:
11851 assert(!var->donotaggr);
11852 assert(var->data.aggregate.var != NULL);
11853 if( var->data.aggregate.scalar > 0.0 )
11854 {
11855 SCIP_CALL( SCIPvarChgBranchDirection(var->data.aggregate.var, branchdirection) );
11856 }
11857 else
11858 {
11859 SCIP_CALL( SCIPvarChgBranchDirection(var->data.aggregate.var, SCIPbranchdirOpposite(branchdirection)) );
11860 }
11861 break;
11862
11863 case SCIP_VARSTATUS_MULTAGGR:
11864 assert(!var->donotmultaggr);
11865 for( v = 0; v < var->data.multaggr.nvars; ++v )
11866 {
11867 /* only update branching direction of aggregation variables, if they don't have a preferred direction yet */
11868 assert(var->data.multaggr.vars[v] != NULL);
11869 if( (SCIP_BRANCHDIR)var->data.multaggr.vars[v]->branchdirection == SCIP_BRANCHDIR_AUTO )
11870 {
11871 if( var->data.multaggr.scalars[v] > 0.0 )
11872 {
11873 SCIP_CALL( SCIPvarChgBranchDirection(var->data.multaggr.vars[v], branchdirection) );
11874 }
11875 else
11876 {
11877 SCIP_CALL( SCIPvarChgBranchDirection(var->data.multaggr.vars[v], SCIPbranchdirOpposite(branchdirection)) );
11878 }
11879 }
11880 }
11881 break;
11882
11883 case SCIP_VARSTATUS_NEGATED:
11884 assert(var->negatedvar != NULL);
11885 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
11886 assert(var->negatedvar->negatedvar == var);
11887 SCIP_CALL( SCIPvarChgBranchDirection(var->negatedvar, SCIPbranchdirOpposite(branchdirection)) );
11888 break;
11889
11890 default:
11891 SCIPerrorMessage("unknown variable status\n");
11892 SCIPABORT();
11893 return SCIP_ERROR; /*lint !e527*/
11894 }
11895
11896 return SCIP_OKAY;
11897 }
11898
11899 /** compares the index of two variables, only active, fixed or negated variables are allowed, if a variable
11900 * is negated then the index of the corresponding active variable is taken, returns -1 if first is
11901 * smaller than, and +1 if first is greater than second variable index; returns 0 if both indices
11902 * are equal, which means both variables are equal
11903 */
11904 int SCIPvarCompareActiveAndNegated(
11905 SCIP_VAR* var1, /**< first problem variable */
11906 SCIP_VAR* var2 /**< second problem variable */
11907 )
11908 {
11909 assert(var1 != NULL);
11910 assert(var2 != NULL);
11911 assert(SCIPvarIsActive(var1) || SCIPvarGetStatus(var1) == SCIP_VARSTATUS_NEGATED || SCIPvarGetStatus(var1) == SCIP_VARSTATUS_FIXED);
11912 assert(SCIPvarIsActive(var2) || SCIPvarGetStatus(var2) == SCIP_VARSTATUS_NEGATED || SCIPvarGetStatus(var2) == SCIP_VARSTATUS_FIXED);
11913
11914 if( SCIPvarGetStatus(var1) == SCIP_VARSTATUS_NEGATED )
11915 var1 = SCIPvarGetNegatedVar(var1);
11916 if( SCIPvarGetStatus(var2) == SCIP_VARSTATUS_NEGATED )
11917 var2 = SCIPvarGetNegatedVar(var2);
11918
11919 assert(var1 != NULL);
11920 assert(var2 != NULL);
11921
11922 if( SCIPvarGetIndex(var1) < SCIPvarGetIndex(var2) )
11923 return -1;
11924 else if( SCIPvarGetIndex(var1) > SCIPvarGetIndex(var2) )
11925 return +1;
11926
11927 assert(var1 == var2);
11928 return 0;
11929 }
11930
11931 /** comparison method for sorting active and negated variables by non-decreasing index, active and negated
11932 * variables are handled as the same variables
11933 */
11934 SCIP_DECL_SORTPTRCOMP(SCIPvarCompActiveAndNegated)
11935 {
11936 return SCIPvarCompareActiveAndNegated((SCIP_VAR*)elem1, (SCIP_VAR*)elem2);
11937 }
11938
11939 /** compares the index of two variables, returns -1 if first is smaller than, and +1 if first is greater than second
11940 * variable index; returns 0 if both indices are equal, which means both variables are equal
11941 */
11942 int SCIPvarCompare(
11943 SCIP_VAR* var1, /**< first problem variable */
11944 SCIP_VAR* var2 /**< second problem variable */
11945 )
11946 {
11947 assert(var1 != NULL);
11948 assert(var2 != NULL);
11949
11950 if( var1->index < var2->index )
11951 return -1;
11952 else if( var1->index > var2->index )
11953 return +1;
11954 else
11955 {
11956 assert(var1 == var2);
11957 return 0;
11958 }
11959 }
11960
11961 /** comparison method for sorting variables by non-decreasing index */
11962 SCIP_DECL_SORTPTRCOMP(SCIPvarComp)
11963 {
11964 return SCIPvarCompare((SCIP_VAR*)elem1, (SCIP_VAR*)elem2);
11965 }
11966
11967 /** comparison method for sorting variables by non-decreasing objective coefficient */
11968 SCIP_DECL_SORTPTRCOMP(SCIPvarCompObj)
11969 {
11970 SCIP_Real obj1;
11971 SCIP_Real obj2;
11972
11973 obj1 = SCIPvarGetObj((SCIP_VAR*)elem1);
11974 obj2 = SCIPvarGetObj((SCIP_VAR*)elem2);
11975
11976 if( obj1 < obj2 )
11977 return -1;
11978 else if( obj1 > obj2 )
11979 return +1;
11980 else
11981 return 0;
11982 }
11983
11984 /** hash key retrieval function for variables */
11985 SCIP_DECL_HASHGETKEY(SCIPvarGetHashkey)
11986 { /*lint --e{715}*/
11987 return elem;
11988 }
11989
11990 /** returns TRUE iff the indices of both variables are equal */
11991 SCIP_DECL_HASHKEYEQ(SCIPvarIsHashkeyEq)
11992 { /*lint --e{715}*/
11993 if( key1 == key2 )
11994 return TRUE;
11995 return FALSE;
11996 }
11997
11998 /** returns the hash value of the key */
11999 SCIP_DECL_HASHKEYVAL(SCIPvarGetHashkeyVal)
12000 { /*lint --e{715}*/
12001 assert( SCIPvarGetIndex((SCIP_VAR*) key) >= 0 );
12002 return (unsigned int) SCIPvarGetIndex((SCIP_VAR*) key);
12003 }
12004
12005 /** return for given variables all their active counterparts; all active variables will be pairwise different */
12006 SCIP_RETCODE SCIPvarsGetActiveVars(
12007 SCIP_SET* set, /**< global SCIP settings */
12008 SCIP_VAR** vars, /**< variable array with given variables and as output all active
12009 * variables, if enough slots exist
12010 */
12011 int* nvars, /**< number of given variables, and as output number of active variables,
12012 * if enough slots exist
12013 */
12014 int varssize, /**< available slots in vars array */
12015 int* requiredsize /**< pointer to store the required array size for the active variables */
12016 )
12017 {
12018 SCIP_VAR** activevars;
12019 int nactivevars;
12020 int activevarssize;
12021
12022 SCIP_VAR* var;
12023 int v;
12024
12025 SCIP_VAR** tmpvars;
12026 SCIP_VAR** multvars;
12027 int tmpvarssize;
12028 int ntmpvars;
12029 int noldtmpvars;
12030 int nmultvars;
12031
12032 assert(set != NULL);
12033 assert(nvars != NULL);
12034 assert(vars != NULL || *nvars == 0);
12035 assert(varssize >= *nvars);
12036 assert(requiredsize != NULL);
12037
12038 *requiredsize = 0;
12039
12040 if( *nvars == 0 )
12041 return SCIP_OKAY;
12042
12043 nactivevars = 0;
12044 activevarssize = *nvars;
12045 ntmpvars = *nvars;
12046 tmpvarssize = *nvars;
12047
12048 /* temporary memory */
12049 SCIP_CALL( SCIPsetAllocBufferArray(set, &activevars, activevarssize) );
12050 /* coverity[copy_paste_error] */
12051 SCIP_CALL( SCIPsetDuplicateBufferArray(set, &tmpvars, vars, ntmpvars) );
12052
12053 noldtmpvars = ntmpvars;
12054
12055 /* sort all variables to combine equal variables easily */
12056 SCIPsortPtr((void**)tmpvars, SCIPvarComp, ntmpvars);
12057 for( v = ntmpvars - 1; v > 0; --v )
12058 {
12059 /* combine same variables */
12060 if( SCIPvarCompare(tmpvars[v], tmpvars[v - 1]) == 0 )
12061 {
12062 --ntmpvars;
12063 tmpvars[v] = tmpvars[ntmpvars];
12064 }
12065 }
12066 /* sort all variables again to combine equal variables later on */
12067 if( noldtmpvars > ntmpvars )
12068 SCIPsortPtr((void**)tmpvars, SCIPvarComp, ntmpvars);
12069
12070 /* collect for each variable the representation in active variables */
12071 while( ntmpvars >= 1 )
12072 {
12073 --ntmpvars;
12074 var = tmpvars[ntmpvars];
12075 assert( var != NULL );
12076
12077 switch( SCIPvarGetStatus(var) )
12078 {
12079 case SCIP_VARSTATUS_ORIGINAL:
12080 if( var->data.original.transvar == NULL )
12081 {
12082 SCIPerrorMessage("original variable has no transformed variable attached\n");
12083 SCIPABORT();
12084 return SCIP_INVALIDDATA; /*lint !e527*/
12085 }
12086 tmpvars[ntmpvars] = var->data.original.transvar;
12087 ++ntmpvars;
12088 break;
12089
12090 case SCIP_VARSTATUS_AGGREGATED:
12091 tmpvars[ntmpvars] = var->data.aggregate.var;
12092 ++ntmpvars;
12093 break;
12094
12095 case SCIP_VARSTATUS_NEGATED:
12096 tmpvars[ntmpvars] = var->negatedvar;
12097 ++ntmpvars;
12098 break;
12099
12100 case SCIP_VARSTATUS_LOOSE:
12101 case SCIP_VARSTATUS_COLUMN:
12102 /* check for space in temporary memory */
12103 if( nactivevars >= activevarssize )
12104 {
12105 activevarssize *= 2;
12106 SCIP_CALL( SCIPsetReallocBufferArray(set, &activevars, activevarssize) );
12107 assert(nactivevars < activevarssize);
12108 }
12109 activevars[nactivevars] = var;
12110 nactivevars++;
12111 break;
12112
12113 case SCIP_VARSTATUS_MULTAGGR:
12114 /* x = a_1*y_1 + ... + a_n*y_n + c */
12115 nmultvars = var->data.multaggr.nvars;
12116 multvars = var->data.multaggr.vars;
12117
12118 /* check for space in temporary memory */
12119 if( nmultvars + ntmpvars > tmpvarssize )
12120 {
12121 while( nmultvars + ntmpvars > tmpvarssize )
12122 tmpvarssize *= 2;
12123 SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpvars, tmpvarssize) );
12124 assert(nmultvars + ntmpvars <= tmpvarssize);
12125 }
12126
12127 /* copy all multi-aggregation variables into our working array */
12128 BMScopyMemoryArray(&tmpvars[ntmpvars], multvars, nmultvars); /*lint !e866*/
12129
12130 /* get active, fixed or multi-aggregated corresponding variables for all new ones */
12131 SCIPvarsGetProbvar(&tmpvars[ntmpvars], nmultvars);
12132
12133 ntmpvars += nmultvars;
12134 noldtmpvars = ntmpvars;
12135
12136 /* sort all variables to combine equal variables easily */
12137 SCIPsortPtr((void**)tmpvars, SCIPvarComp, ntmpvars);
12138 for( v = ntmpvars - 1; v > 0; --v )
12139 {
12140 /* combine same variables */
12141 if( SCIPvarCompare(tmpvars[v], tmpvars[v - 1]) == 0 )
12142 {
12143 --ntmpvars;
12144 tmpvars[v] = tmpvars[ntmpvars];
12145 }
12146 }
12147 /* sort all variables again to combine equal variables later on */
12148 if( noldtmpvars > ntmpvars )
12149 SCIPsortPtr((void**)tmpvars, SCIPvarComp, ntmpvars);
12150
12151 break;
12152
12153 case SCIP_VARSTATUS_FIXED:
12154 /* no need for memorizing fixed variables */
12155 break;
12156
12157 default:
12158 SCIPerrorMessage("unknown variable status\n");
12159 SCIPABORT();
12160 return SCIP_INVALIDDATA; /*lint !e527*/
12161 }
12162 }
12163
12164 /* sort variable array by variable index */
12165 SCIPsortPtr((void**)activevars, SCIPvarComp, nactivevars);
12166
12167 /* eliminate duplicates and count required size */
12168 v = nactivevars - 1;
12169 while( v > 0 )
12170 {
12171 /* combine both variable since they are the same */
12172 if( SCIPvarCompare(activevars[v - 1], activevars[v]) == 0 )
12173 {
12174 --nactivevars;
12175 activevars[v] = activevars[nactivevars];
12176 }
12177 --v;
12178 }
12179 *requiredsize = nactivevars;
12180
12181 if( varssize >= *requiredsize )
12182 {
12183 assert(vars != NULL);
12184
12185 *nvars = *requiredsize;
12186 BMScopyMemoryArray(vars, activevars, nactivevars);
12187 }
12188
12189 SCIPsetFreeBufferArray(set, &tmpvars);
12190 SCIPsetFreeBufferArray(set, &activevars);
12191
12192 return SCIP_OKAY;
12193 }
12194
12195 /** gets corresponding active, fixed, or multi-aggregated problem variables of given variables,
12196 * @note the content of the given array will/might change
12197 */
12198 void SCIPvarsGetProbvar(
12199 SCIP_VAR** vars, /**< array of problem variables */
12200 int nvars /**< number of variables */
12201 )
12202 {
12203 int v;
12204
12205 assert(vars != NULL || nvars == 0);
12206
12207 for( v = nvars - 1; v >= 0; --v )
12208 {
12209 assert(vars != NULL);
12210 assert(vars[v] != NULL);
12211
12212 vars[v] = SCIPvarGetProbvar(vars[v]);
12213 assert(vars[v] != NULL);
12214 }
12215 }
12216
12217 /** gets corresponding active, fixed, or multi-aggregated problem variable of a variable */
12218 SCIP_VAR* SCIPvarGetProbvar(
12219 SCIP_VAR* var /**< problem variable */
12220 )
12221 {
12222 SCIP_VAR* retvar;
12223
12224 assert(var != NULL);
12225
12226 retvar = var;
12227
12228 SCIPdebugMessage("get problem variable of <%s>\n", var->name);
12229
12230 while( TRUE ) /*lint !e716 */
12231 {
12232 assert(retvar != NULL);
12233
12234 switch( SCIPvarGetStatus(retvar) )
12235 {
12236 case SCIP_VARSTATUS_ORIGINAL:
12237 if( retvar->data.original.transvar == NULL )
12238 {
12239 SCIPerrorMessage("original variable has no transformed variable attached\n");
12240 SCIPABORT();
12241 return NULL; /*lint !e527 */
12242 }
12243 retvar = retvar->data.original.transvar;
12244 break;
12245
12246 case SCIP_VARSTATUS_LOOSE:
12247 case SCIP_VARSTATUS_COLUMN:
12248 case SCIP_VARSTATUS_FIXED:
12249 return retvar;
12250
12251 case SCIP_VARSTATUS_MULTAGGR:
12252 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
12253 if ( retvar->data.multaggr.nvars == 1 )
12254 retvar = retvar->data.multaggr.vars[0];
12255 else
12256 return retvar;
12257 break;
12258
12259 case SCIP_VARSTATUS_AGGREGATED:
12260 retvar = retvar->data.aggregate.var;
12261 break;
12262
12263 case SCIP_VARSTATUS_NEGATED:
12264 retvar = retvar->negatedvar;
12265 break;
12266
12267 default:
12268 SCIPerrorMessage("unknown variable status\n");
12269 SCIPABORT();
12270 return NULL; /*lint !e527*/
12271 }
12272 }
12273 }
12274
12275 /** gets corresponding active, fixed, or multi-aggregated problem variables of binary variables and updates the given
12276 * negation status of each variable
12277 */
12278 SCIP_RETCODE SCIPvarsGetProbvarBinary(
12279 SCIP_VAR*** vars, /**< pointer to binary problem variables */
12280 SCIP_Bool** negatedarr, /**< pointer to corresponding array to update the negation status */
12281 int nvars /**< number of variables and values in vars and negated array */
12282 )
12283 {
12284 SCIP_VAR** var;
12285 SCIP_Bool* negated;
12286 int v;
12287
12288 assert(vars != NULL);
12289 assert(*vars != NULL || nvars == 0);
12290 assert(negatedarr != NULL);
12291 assert(*negatedarr != NULL || nvars == 0);
12292
12293 for( v = nvars - 1; v >= 0; --v )
12294 {
12295 var = &((*vars)[v]);
12296 negated = &((*negatedarr)[v]);
12297
12298 /* get problem variable */
12299 SCIP_CALL( SCIPvarGetProbvarBinary(var, negated) );
12300 }
12301
12302 return SCIP_OKAY;
12303 }
12304
12305
12306 /** gets corresponding active, fixed, or multi-aggregated problem variable of a binary variable and updates the given
12307 * negation status (this means you have to assign a value to SCIP_Bool negated before calling this method, usually
12308 * FALSE is used)
12309 */
12310 SCIP_RETCODE SCIPvarGetProbvarBinary(
12311 SCIP_VAR** var, /**< pointer to binary problem variable */
12312 SCIP_Bool* negated /**< pointer to update the negation status */
12313 )
12314 {
12315 SCIP_Bool active = FALSE;
12316 #ifndef NDEBUG
12317 SCIP_Real constant = 0.0;
12318 SCIP_Bool orignegated;
12319 #endif
12320
12321 assert(var != NULL);
12322 assert(*var != NULL);
12323 assert(negated != NULL);
12324 assert(SCIPvarIsBinary(*var));
12325
12326 #ifndef NDEBUG
12327 orignegated = *negated;
12328 #endif
12329
12330 while( !active && *var != NULL )
12331 {
12332 switch( SCIPvarGetStatus(*var) )
12333 {
12334 case SCIP_VARSTATUS_ORIGINAL:
12335 if( (*var)->data.original.transvar == NULL )
12336 return SCIP_OKAY;
12337 *var = (*var)->data.original.transvar;
12338 break;
12339
12340 case SCIP_VARSTATUS_LOOSE:
12341 case SCIP_VARSTATUS_COLUMN:
12342 case SCIP_VARSTATUS_FIXED:
12343 active = TRUE;
12344 break;
12345
12346 case SCIP_VARSTATUS_MULTAGGR:
12347 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
12348 if ( (*var)->data.multaggr.nvars == 1 )
12349 {
12350 assert( (*var)->data.multaggr.vars != NULL );
12351 assert( (*var)->data.multaggr.scalars != NULL );
12352 assert( SCIPvarIsBinary((*var)->data.multaggr.vars[0]) );
12353 assert(!EPSZ((*var)->data.multaggr.scalars[0], 1e-06));
12354
12355 /* if not all variables were fully propagated, it might happen that a variable is multi-aggregated to
12356 * another variable which needs to be fixed
12357 *
12358 * e.g. x = y - 1 => (x = 0 && y = 1)
12359 * e.g. x = y + 1 => (x = 1 && y = 0)
12360 *
12361 * is this special case we need to return the muti-aggregation
12362 */
12363 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)) )
12364 {
12365 assert(EPSEQ((*var)->data.multaggr.scalars[0], 1.0, 1e-06));
12366 }
12367 else
12368 {
12369 /* @note due to fixations, a multi-aggregation can have a constant of zero and a negative scalar or even
12370 * a scalar in absolute value unequal to one, in this case this aggregation variable needs to be
12371 * fixed to zero, but this should be done by another enforcement; so not depending on the scalar,
12372 * we will return the aggregated variable;
12373 */
12374 if( !EPSEQ(REALABS((*var)->data.multaggr.scalars[0]), 1.0, 1e-06) )
12375 {
12376 active = TRUE;
12377 break;
12378 }
12379
12380 /* @note it may also happen that the constant is larger than 1 or smaller than 0, in that case the
12381 * aggregation variable needs to be fixed to one, but this should be done by another enforcement;
12382 * so if this is the case, we will return the aggregated variable
12383 */
12384 assert(EPSZ((*var)->data.multaggr.constant, 1e-06) || EPSEQ((*var)->data.multaggr.constant, 1.0, 1e-06)
12385 || EPSZ((*var)->data.multaggr.constant + (*var)->data.multaggr.scalars[0], 1e-06)
12386 || EPSEQ((*var)->data.multaggr.constant + (*var)->data.multaggr.scalars[0], 1.0, 1e-06));
12387
12388 if( !EPSZ((*var)->data.multaggr.constant, 1e-06) && !EPSEQ((*var)->data.multaggr.constant, 1.0, 1e-06) )
12389 {
12390 active = TRUE;
12391 break;
12392 }
12393
12394 assert(EPSEQ((*var)->data.multaggr.scalars[0], 1.0, 1e-06) || EPSEQ((*var)->data.multaggr.scalars[0], -1.0, 1e-06));
12395
12396 if( EPSZ((*var)->data.multaggr.constant, 1e-06) )
12397 {
12398 /* if the scalar is negative, either the aggregation variable is already fixed to zero or has at
12399 * least one uplock (that hopefully will enforce this fixation to zero); can it happen that this
12400 * variable itself is multi-aggregated again?
12401 */
12402 assert(EPSEQ((*var)->data.multaggr.scalars[0], -1.0, 1e-06) ?
12403 ((SCIPvarGetUbGlobal((*var)->data.multaggr.vars[0]) < 0.5) ||
12404 SCIPvarGetNLocksUpType((*var)->data.multaggr.vars[0], SCIP_LOCKTYPE_MODEL) > 0) : TRUE);
12405 }
12406 else
12407 {
12408 assert(EPSEQ((*var)->data.multaggr.scalars[0], -1.0, 1e-06));
12409 #ifndef NDEBUG
12410 constant += (*negated) != orignegated ? -1.0 : 1.0;
12411 #endif
12412
12413 *negated = !(*negated);
12414 }
12415 *var = (*var)->data.multaggr.vars[0];
12416 break;
12417 }
12418 }
12419 active = TRUE; /*lint !e838*/
12420 break;
12421
12422 case SCIP_VARSTATUS_AGGREGATED: /* x = a'*x' + c' => a*x + c == (a*a')*x' + (a*c' + c) */
12423 assert((*var)->data.aggregate.var != NULL);
12424 assert(EPSEQ((*var)->data.aggregate.scalar, 1.0, 1e-06) || EPSEQ((*var)->data.aggregate.scalar, -1.0, 1e-06));
12425 assert(EPSLE((*var)->data.aggregate.var->glbdom.ub - (*var)->data.aggregate.var->glbdom.lb, 1.0, 1e-06));
12426 #ifndef NDEBUG
12427 constant += (*negated) != orignegated ? -(*var)->data.aggregate.constant : (*var)->data.aggregate.constant;
12428 #endif
12429
12430 *negated = ((*var)->data.aggregate.scalar > 0.0) ? *negated : !(*negated);
12431 *var = (*var)->data.aggregate.var;
12432 break;
12433
12434 case SCIP_VARSTATUS_NEGATED: /* x = - x' + c' => a*x + c == (-a)*x' + (a*c' + c) */
12435 assert((*var)->negatedvar != NULL);
12436 #ifndef NDEBUG
12437 constant += (*negated) != orignegated ? -1.0 : 1.0;
12438 #endif
12439
12440 *negated = !(*negated);
12441 *var = (*var)->negatedvar;
12442 break;
12443
12444 default:
12445 SCIPerrorMessage("unknown variable status\n");
12446 return SCIP_INVALIDDATA;
12447 }
12448 }
12449 assert(active == (*var != NULL));
12450
12451 if( active )
12452 {
12453 assert(SCIPvarIsBinary(*var));
12454 assert(EPSZ(constant, 1e-06) || EPSEQ(constant, 1.0, 1e-06));
12455 assert(EPSZ(constant, 1e-06) == ((*negated) == orignegated));
12456
12457 return SCIP_OKAY;
12458 }
12459 else
12460 {
12461 SCIPerrorMessage("active variable path leads to NULL pointer\n");
12462 return SCIP_INVALIDDATA;
12463 }
12464 }
12465
12466 /** transforms given variable, boundtype and bound to the corresponding active, fixed, or multi-aggregated variable
12467 * values
12468 */
12469 SCIP_RETCODE SCIPvarGetProbvarBound(
12470 SCIP_VAR** var, /**< pointer to problem variable */
12471 SCIP_Real* bound, /**< pointer to bound value to transform */
12472 SCIP_BOUNDTYPE* boundtype /**< pointer to type of bound: lower or upper bound */
12473 )
12474 {
12475 assert(var != NULL);
12476 assert(*var != NULL);
12477 assert(bound != NULL);
12478 assert(boundtype != NULL);
12479
12480 SCIPdebugMessage("get probvar bound %g of type %d of variable <%s>\n", *bound, *boundtype, (*var)->name);
12481
12482 switch( SCIPvarGetStatus(*var) )
12483 {
12484 case SCIP_VARSTATUS_ORIGINAL:
12485 if( (*var)->data.original.transvar == NULL )
12486 {
12487 SCIPerrorMessage("original variable has no transformed variable attached\n");
12488 return SCIP_INVALIDDATA;
12489 }
12490 *var = (*var)->data.original.transvar;
12491 SCIP_CALL( SCIPvarGetProbvarBound(var, bound, boundtype) );
12492 break;
12493
12494 case SCIP_VARSTATUS_LOOSE:
12495 case SCIP_VARSTATUS_COLUMN:
12496 case SCIP_VARSTATUS_FIXED:
12497 break;
12498
12499 case SCIP_VARSTATUS_MULTAGGR:
12500 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
12501 if ( (*var)->data.multaggr.nvars == 1 )
12502 {
12503 assert( (*var)->data.multaggr.vars != NULL );
12504 assert( (*var)->data.multaggr.scalars != NULL );
12505 assert( (*var)->data.multaggr.scalars[0] != 0.0 );
12506
12507 (*bound) /= (*var)->data.multaggr.scalars[0];
12508 (*bound) -= (*var)->data.multaggr.constant/(*var)->data.multaggr.scalars[0];
12509 if ( (*var)->data.multaggr.scalars[0] < 0.0 )
12510 {
12511 if ( *boundtype == SCIP_BOUNDTYPE_LOWER )
12512 *boundtype = SCIP_BOUNDTYPE_UPPER;
12513 else
12514 *boundtype = SCIP_BOUNDTYPE_LOWER;
12515 }
12516 *var = (*var)->data.multaggr.vars[0];
12517 SCIP_CALL( SCIPvarGetProbvarBound(var, bound, boundtype) );
12518 }
12519 break;
12520
12521 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = x/a - c/a */
12522 assert((*var)->data.aggregate.var != NULL);
12523 assert((*var)->data.aggregate.scalar != 0.0);
12524
12525 (*bound) /= (*var)->data.aggregate.scalar;
12526 (*bound) -= (*var)->data.aggregate.constant/(*var)->data.aggregate.scalar;
12527 if( (*var)->data.aggregate.scalar < 0.0 )
12528 {
12529 if( *boundtype == SCIP_BOUNDTYPE_LOWER )
12530 *boundtype = SCIP_BOUNDTYPE_UPPER;
12531 else
12532 *boundtype = SCIP_BOUNDTYPE_LOWER;
12533 }
12534 *var = (*var)->data.aggregate.var;
12535 SCIP_CALL( SCIPvarGetProbvarBound(var, bound, boundtype) );
12536 break;
12537
12538 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
12539 assert((*var)->negatedvar != NULL);
12540 assert(SCIPvarGetStatus((*var)->negatedvar) != SCIP_VARSTATUS_NEGATED);
12541 assert((*var)->negatedvar->negatedvar == *var);
12542 (*bound) = (*var)->data.negate.constant - *bound;
12543 if( *boundtype == SCIP_BOUNDTYPE_LOWER )
12544 *boundtype = SCIP_BOUNDTYPE_UPPER;
12545 else
12546 *boundtype = SCIP_BOUNDTYPE_LOWER;
12547 *var = (*var)->negatedvar;
12548 SCIP_CALL( SCIPvarGetProbvarBound(var, bound, boundtype) );
12549 break;
12550
12551 default:
12552 SCIPerrorMessage("unknown variable status\n");
12553 return SCIP_INVALIDDATA;
12554 }
12555
12556 return SCIP_OKAY;
12557 }
12558
12559 /** transforms given variable and domain hole to the corresponding active, fixed, or multi-aggregated variable
12560 * values
12561 */
12562 SCIP_RETCODE SCIPvarGetProbvarHole(
12563 SCIP_VAR** var, /**< pointer to problem variable */
12564 SCIP_Real* left, /**< pointer to left bound of open interval in hole to transform */
12565 SCIP_Real* right /**< pointer to right bound of open interval in hole to transform */
12566 )
12567 {
12568 assert(var != NULL);
12569 assert(*var != NULL);
12570 assert(left != NULL);
12571 assert(right != NULL);
12572
12573 SCIPdebugMessage("get probvar hole (%g,%g) of variable <%s>\n", *left, *right, (*var)->name);
12574
12575 switch( SCIPvarGetStatus(*var) )
12576 {
12577 case SCIP_VARSTATUS_ORIGINAL:
12578 if( (*var)->data.original.transvar == NULL )
12579 {
12580 SCIPerrorMessage("original variable has no transformed variable attached\n");
12581 return SCIP_INVALIDDATA;
12582 }
12583 *var = (*var)->data.original.transvar;
12584 SCIP_CALL( SCIPvarGetProbvarHole(var, left, right) );
12585 break;
12586
12587 case SCIP_VARSTATUS_LOOSE:
12588 case SCIP_VARSTATUS_COLUMN:
12589 case SCIP_VARSTATUS_FIXED:
12590 case SCIP_VARSTATUS_MULTAGGR:
12591 break;
12592
12593 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = x/a - c/a */
12594 assert((*var)->data.aggregate.var != NULL);
12595 assert((*var)->data.aggregate.scalar != 0.0);
12596
12597 /* scale back */
12598 (*left) /= (*var)->data.aggregate.scalar;
12599 (*right) /= (*var)->data.aggregate.scalar;
12600
12601 /* shift back */
12602 (*left) -= (*var)->data.aggregate.constant/(*var)->data.aggregate.scalar;
12603 (*right) -= (*var)->data.aggregate.constant/(*var)->data.aggregate.scalar;
12604
12605 *var = (*var)->data.aggregate.var;
12606
12607 /* check if the interval bounds have to swapped */
12608 if( (*var)->data.aggregate.scalar < 0.0 )
12609 {
12610 SCIP_CALL( SCIPvarGetProbvarHole(var, right, left) );
12611 }
12612 else
12613 {
12614 SCIP_CALL( SCIPvarGetProbvarHole(var, left, right) );
12615 }
12616 break;
12617
12618 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
12619 assert((*var)->negatedvar != NULL);
12620 assert(SCIPvarGetStatus((*var)->negatedvar) != SCIP_VARSTATUS_NEGATED);
12621 assert((*var)->negatedvar->negatedvar == *var);
12622
12623 /* shift and scale back */
12624 (*left) = (*var)->data.negate.constant - (*left);
12625 (*right) = (*var)->data.negate.constant - (*right);
12626
12627 *var = (*var)->negatedvar;
12628
12629 /* through the negated variable the left and right interval bound have to swapped */
12630 SCIP_CALL( SCIPvarGetProbvarHole(var, right, left) );
12631 break;
12632
12633 default:
12634 SCIPerrorMessage("unknown variable status\n");
12635 return SCIP_INVALIDDATA;
12636 }
12637
12638 return SCIP_OKAY;
12639 }
12640
12641 /** transforms given variable, scalar and constant to the corresponding active, fixed, or
12642 * multi-aggregated variable, scalar and constant; if the variable resolves to a fixed variable,
12643 * "scalar" will be 0.0 and the value of the sum will be stored in "constant"; a multi-aggregation
12644 * with only one active variable (this can happen due to fixings after the multi-aggregation),
12645 * is treated like an aggregation; if the multi-aggregation constant is infinite, "scalar" will be 0.0
12646 */
12647 SCIP_RETCODE SCIPvarGetProbvarSum(
12648 SCIP_VAR** var, /**< pointer to problem variable x in sum a*x + c */
12649 SCIP_SET* set, /**< global SCIP settings */
12650 SCIP_Real* scalar, /**< pointer to scalar a in sum a*x + c */
12651 SCIP_Real* constant /**< pointer to constant c in sum a*x + c */
12652 )
12653 {
12654 assert(var != NULL);
12655 assert(scalar != NULL);
12656 assert(constant != NULL);
12657
12658 while( *var != NULL )
12659 {
12660 switch( SCIPvarGetStatus(*var) )
12661 {
12662 case SCIP_VARSTATUS_ORIGINAL:
12663 if( (*var)->data.original.transvar == NULL )
12664 {
12665 SCIPerrorMessage("original variable has no transformed variable attached\n");
12666 return SCIP_INVALIDDATA;
12667 }
12668 *var = (*var)->data.original.transvar;
12669 break;
12670
12671 case SCIP_VARSTATUS_LOOSE:
12672 case SCIP_VARSTATUS_COLUMN:
12673 return SCIP_OKAY;
12674
12675 case SCIP_VARSTATUS_FIXED: /* x = c' => a*x + c == (a*c' + c) */
12676 if( !SCIPsetIsInfinity(set, (*constant)) && !SCIPsetIsInfinity(set, -(*constant)) )
12677 {
12678 if( SCIPsetIsInfinity(set, (*var)->glbdom.lb) || SCIPsetIsInfinity(set, -((*var)->glbdom.lb)) )
12679 {
12680 assert(*scalar != 0.0);
12681 if( (*scalar) * (*var)->glbdom.lb > 0.0 )
12682 (*constant) = SCIPsetInfinity(set);
12683 else
12684 (*constant) = -SCIPsetInfinity(set);
12685 }
12686 else
12687 (*constant) += *scalar * (*var)->glbdom.lb;
12688 }
12689 #ifndef NDEBUG
12690 else
12691 {
12692 assert(!SCIPsetIsInfinity(set, (*constant)) || !((*scalar) * (*var)->glbdom.lb < 0.0 &&
12693 (SCIPsetIsInfinity(set, (*var)->glbdom.lb) || SCIPsetIsInfinity(set, -((*var)->glbdom.lb)))));
12694 assert(!SCIPsetIsInfinity(set, -(*constant)) || !((*scalar) * (*var)->glbdom.lb > 0.0 &&
12695 (SCIPsetIsInfinity(set, (*var)->glbdom.lb) || SCIPsetIsInfinity(set, -((*var)->glbdom.lb)))));
12696 }
12697 #endif
12698 *scalar = 0.0;
12699 return SCIP_OKAY;
12700
12701 case SCIP_VARSTATUS_MULTAGGR:
12702 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
12703 if ( (*var)->data.multaggr.nvars == 1 )
12704 {
12705 assert((*var)->data.multaggr.vars != NULL);
12706 assert((*var)->data.multaggr.scalars != NULL);
12707 assert((*var)->data.multaggr.vars[0] != NULL);
12708 if( !SCIPsetIsInfinity(set, (*constant)) && !SCIPsetIsInfinity(set, -(*constant)) )
12709 {
12710 /* the multi-aggregation constant can be infinite, if one of the multi-aggregation variables
12711 * was fixed to +/-infinity; ensure that the constant is set to +/-infinity, too, and the scalar
12712 * is set to 0.0, because the multi-aggregated variable can be seen as fixed, too
12713 */
12714 if( SCIPsetIsInfinity(set, (*var)->data.multaggr.constant)
12715 || SCIPsetIsInfinity(set, -((*var)->data.multaggr.constant)) )
12716 {
12717 if( (*scalar) * (*var)->data.multaggr.constant > 0 )
12718 {
12719 assert(!SCIPsetIsInfinity(set, -(*constant)));
12720 (*constant) = SCIPsetInfinity(set);
12721 }
12722 else
12723 {
12724 assert(!SCIPsetIsInfinity(set, *constant));
12725 (*constant) = -SCIPsetInfinity(set);
12726 }
12727 (*scalar) = 0.0;
12728 }
12729 else
12730 (*constant) += *scalar * (*var)->data.multaggr.constant;
12731 }
12732 (*scalar) *= (*var)->data.multaggr.scalars[0];
12733 *var = (*var)->data.multaggr.vars[0];
12734 break;
12735 }
12736 return SCIP_OKAY;
12737
12738 case SCIP_VARSTATUS_AGGREGATED: /* x = a'*x' + c' => a*x + c == (a*a')*x' + (a*c' + c) */
12739 assert((*var)->data.aggregate.var != NULL);
12740 assert(!SCIPsetIsInfinity(set, (*var)->data.aggregate.constant)
12741 && !SCIPsetIsInfinity(set, (*var)->data.aggregate.constant));
12742 if( !SCIPsetIsInfinity(set, (*constant)) && !SCIPsetIsInfinity(set, -(*constant)) )
12743 (*constant) += *scalar * (*var)->data.aggregate.constant;
12744 (*scalar) *= (*var)->data.aggregate.scalar;
12745 *var = (*var)->data.aggregate.var;
12746 break;
12747
12748 case SCIP_VARSTATUS_NEGATED: /* x = - x' + c' => a*x + c == (-a)*x' + (a*c' + c) */
12749 assert((*var)->negatedvar != NULL);
12750 assert(SCIPvarGetStatus((*var)->negatedvar) != SCIP_VARSTATUS_NEGATED);
12751 assert((*var)->negatedvar->negatedvar == *var);
12752 assert(!SCIPsetIsInfinity(set, (*var)->data.negate.constant)
12753 && !SCIPsetIsInfinity(set, (*var)->data.negate.constant));
12754 if( !SCIPsetIsInfinity(set, (*constant)) && !SCIPsetIsInfinity(set, -(*constant)) )
12755 (*constant) += *scalar * (*var)->data.negate.constant;
12756 (*scalar) *= -1.0;
12757 *var = (*var)->negatedvar;
12758 break;
12759
12760 default:
12761 SCIPerrorMessage("unknown variable status\n");
12762 SCIPABORT();
12763 return SCIP_INVALIDDATA; /*lint !e527*/
12764 }
12765 }
12766 *scalar = 0.0;
12767
12768 return SCIP_OKAY;
12769 }
12770
12771 /** retransforms given variable, scalar and constant to the corresponding original variable, scalar
12772 * and constant, if possible; if the retransformation is impossible, NULL is returned as variable
12773 */
12774 SCIP_RETCODE SCIPvarGetOrigvarSum(
12775 SCIP_VAR** var, /**< pointer to problem variable x in sum a*x + c */
12776 SCIP_Real* scalar, /**< pointer to scalar a in sum a*x + c */
12777 SCIP_Real* constant /**< pointer to constant c in sum a*x + c */
12778 )
12779 {
12780 SCIP_VAR* parentvar;
12781
12782 assert(var != NULL);
12783 assert(*var != NULL);
12784 assert(scalar != NULL);
12785 assert(constant != NULL);
12786
12787 while( !SCIPvarIsOriginal(*var) )
12788 {
12789 /* if the variable has no parent variables, it was generated during solving and has no corresponding original
12790 * var
12791 */
12792 if( (*var)->nparentvars == 0 )
12793 {
12794 /* negated variables do not need to have a parent variables, and negated variables can exist in original
12795 * space
12796 */
12797 if( SCIPvarGetStatus(*var) == SCIP_VARSTATUS_NEGATED &&
12798 ((*var)->negatedvar->nparentvars == 0 || (*var)->negatedvar->parentvars[0] != *var) )
12799 {
12800 *scalar *= -1.0;
12801 *constant -= (*var)->data.negate.constant * (*scalar);
12802 *var = (*var)->negatedvar;
12803
12804 continue;
12805 }
12806 /* if the variables does not have any parent the variables was created during solving and has no original
12807 * counterpart
12808 */
12809 else
12810 {
12811 *var = NULL;
12812
12813 return SCIP_OKAY;
12814 }
12815 }
12816
12817 /* follow the link to the first parent variable */
12818 parentvar = (*var)->parentvars[0];
12819 assert(parentvar != NULL);
12820
12821 switch( SCIPvarGetStatus(parentvar) )
12822 {
12823 case SCIP_VARSTATUS_ORIGINAL:
12824 break;
12825
12826 case SCIP_VARSTATUS_COLUMN:
12827 case SCIP_VARSTATUS_LOOSE:
12828 case SCIP_VARSTATUS_FIXED:
12829 case SCIP_VARSTATUS_MULTAGGR:
12830 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
12831 return SCIP_INVALIDDATA;
12832
12833 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + b -> y = (x-b)/a, s*y + c = (s/a)*x + c-b*s/a */
12834 assert(parentvar->data.aggregate.var == *var);
12835 assert(parentvar->data.aggregate.scalar != 0.0);
12836 *scalar /= parentvar->data.aggregate.scalar;
12837 *constant -= parentvar->data.aggregate.constant * (*scalar);
12838 break;
12839
12840 case SCIP_VARSTATUS_NEGATED: /* x = b - y -> y = b - x, s*y + c = -s*x + c+b*s */
12841 assert(parentvar->negatedvar != NULL);
12842 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
12843 assert(parentvar->negatedvar->negatedvar == parentvar);
12844 *scalar *= -1.0;
12845 *constant -= parentvar->data.negate.constant * (*scalar);
12846 break;
12847
12848 default:
12849 SCIPerrorMessage("unknown variable status\n");
12850 return SCIP_INVALIDDATA;
12851 }
12852
12853 assert( parentvar != NULL );
12854 *var = parentvar;
12855 }
12856
12857 return SCIP_OKAY;
12858 }
12859
12860 /** returns whether the given variable is the direct counterpart of an original problem variable */
12861 SCIP_Bool SCIPvarIsTransformedOrigvar(
12862 SCIP_VAR* var /**< problem variable */
12863 )
12864 {
12865 SCIP_VAR* parentvar;
12866 assert(var != NULL);
12867
12868 if( !SCIPvarIsTransformed(var) || var->nparentvars < 1 )
12869 return FALSE;
12870
12871 assert(var->parentvars != NULL);
12872 parentvar = var->parentvars[0];
12873 assert(parentvar != NULL);
12874
12875 /* we follow the aggregation tree to the root unless an original variable has been found - the first entries in the parentlist are candidates */
12876 while( parentvar->nparentvars >= 1 && SCIPvarGetStatus(parentvar) != SCIP_VARSTATUS_ORIGINAL )
12877 parentvar = parentvar->parentvars[0];
12878 assert( parentvar != NULL );
12879
12880 return ( SCIPvarGetStatus(parentvar) == SCIP_VARSTATUS_ORIGINAL );
12881 }
12882
12883 /** gets objective value of variable in current SCIP_LP; the value can be different from the objective value stored in
12884 * the variable's own data due to diving, that operate only on the LP without updating the variables
12885 */
12886 SCIP_Real SCIPvarGetObjLP(
12887 SCIP_VAR* var /**< problem variable */
12888 )
12889 {
12890 assert(var != NULL);
12891
12892 /* get bounds of attached variables */
12893 switch( SCIPvarGetStatus(var) )
12894 {
12895 case SCIP_VARSTATUS_ORIGINAL:
12896 assert(var->data.original.transvar != NULL);
12897 return SCIPvarGetObjLP(var->data.original.transvar);
12898
12899 case SCIP_VARSTATUS_COLUMN:
12900 assert(var->data.col != NULL);
12901 return SCIPcolGetObj(var->data.col);
12902
12903 case SCIP_VARSTATUS_LOOSE:
12904 case SCIP_VARSTATUS_FIXED:
12905 return var->obj;
12906
12907 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
12908 assert(var->data.aggregate.var != NULL);
12909 return var->data.aggregate.scalar * SCIPvarGetObjLP(var->data.aggregate.var);
12910
12911 case SCIP_VARSTATUS_MULTAGGR:
12912 SCIPerrorMessage("cannot get the objective value of a multiple aggregated variable\n");
12913 SCIPABORT();
12914 return 0.0; /*lint !e527*/
12915
12916 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
12917 assert(var->negatedvar != NULL);
12918 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
12919 assert(var->negatedvar->negatedvar == var);
12920 return -SCIPvarGetObjLP(var->negatedvar);
12921
12922 default:
12923 SCIPerrorMessage("unknown variable status\n");
12924 SCIPABORT();
12925 return 0.0; /*lint !e527*/
12926 }
12927 }
12928
12929 /** gets lower bound of variable in current SCIP_LP; the bound can be different from the bound stored in the variable's own
12930 * data due to diving or conflict analysis, that operate only on the LP without updating the variables
12931 */
12932 SCIP_Real SCIPvarGetLbLP(
12933 SCIP_VAR* var, /**< problem variable */
12934 SCIP_SET* set /**< global SCIP settings */
12935 )
12936 {
12937 assert(var != NULL);
12938 assert(set != NULL);
12939 assert(var->scip == set->scip);
12940
12941 /* get bounds of attached variables */
12942 switch( SCIPvarGetStatus(var) )
12943 {
12944 case SCIP_VARSTATUS_ORIGINAL:
12945 assert(var->data.original.transvar != NULL);
12946 return SCIPvarGetLbLP(var->data.original.transvar, set);
12947
12948 case SCIP_VARSTATUS_COLUMN:
12949 assert(var->data.col != NULL);
12950 return SCIPcolGetLb(var->data.col);
12951
12952 case SCIP_VARSTATUS_LOOSE:
12953 case SCIP_VARSTATUS_FIXED:
12954 return var->locdom.lb;
12955
12956 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
12957 assert(var->data.aggregate.var != NULL);
12958 if( (var->data.aggregate.scalar > 0.0 && SCIPsetIsInfinity(set, -SCIPvarGetLbLP(var->data.aggregate.var, set)))
12959 || (var->data.aggregate.scalar < 0.0 && SCIPsetIsInfinity(set, SCIPvarGetUbLP(var->data.aggregate.var, set))) )
12960 {
12961 return -SCIPsetInfinity(set);
12962 }
12963 else if( var->data.aggregate.scalar > 0.0 )
12964 {
12965 /* a > 0 -> get lower bound of y */
12966 return var->data.aggregate.scalar * SCIPvarGetLbLP(var->data.aggregate.var, set) + var->data.aggregate.constant;
12967 }
12968 else if( var->data.aggregate.scalar < 0.0 )
12969 {
12970 /* a < 0 -> get upper bound of y */
12971 return var->data.aggregate.scalar * SCIPvarGetUbLP(var->data.aggregate.var, set) + var->data.aggregate.constant;
12972 }
12973 else
12974 {
12975 SCIPerrorMessage("scalar is zero in aggregation\n");
12976 SCIPABORT();
12977 return SCIP_INVALID; /*lint !e527*/
12978 }
12979
12980 case SCIP_VARSTATUS_MULTAGGR:
12981 /**@todo get the sides of the corresponding linear constraint */
12982 SCIPerrorMessage("getting the bounds of a multiple aggregated variable is not implemented yet\n");
12983 SCIPABORT();
12984 return SCIP_INVALID; /*lint !e527*/
12985
12986 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
12987 assert(var->negatedvar != NULL);
12988 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
12989 assert(var->negatedvar->negatedvar == var);
12990 return var->data.negate.constant - SCIPvarGetUbLP(var->negatedvar, set);
12991
12992 default:
12993 SCIPerrorMessage("unknown variable status\n");
12994 SCIPABORT();
12995 return SCIP_INVALID; /*lint !e527*/
12996 }
12997 }
12998
12999 /** gets upper bound of variable in current SCIP_LP; the bound can be different from the bound stored in the variable's own
13000 * data due to diving or conflict analysis, that operate only on the LP without updating the variables
13001 */
13002 SCIP_Real SCIPvarGetUbLP(
13003 SCIP_VAR* var, /**< problem variable */
13004 SCIP_SET* set /**< global SCIP settings */
13005 )
13006 {
13007 assert(var != NULL);
13008 assert(set != NULL);
13009 assert(var->scip == set->scip);
13010
13011 /* get bounds of attached variables */
13012 switch( SCIPvarGetStatus(var) )
13013 {
13014 case SCIP_VARSTATUS_ORIGINAL:
13015 assert(var->data.original.transvar != NULL);
13016 return SCIPvarGetUbLP(var->data.original.transvar, set);
13017
13018 case SCIP_VARSTATUS_COLUMN:
13019 assert(var->data.col != NULL);
13020 return SCIPcolGetUb(var->data.col);
13021
13022 case SCIP_VARSTATUS_LOOSE:
13023 case SCIP_VARSTATUS_FIXED:
13024 return var->locdom.ub;
13025
13026 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
13027 assert(var->data.aggregate.var != NULL);
13028 if( (var->data.aggregate.scalar > 0.0 && SCIPsetIsInfinity(set, SCIPvarGetUbLP(var->data.aggregate.var, set)))
13029 || (var->data.aggregate.scalar < 0.0 && SCIPsetIsInfinity(set, -SCIPvarGetLbLP(var->data.aggregate.var, set))) )
13030 {
13031 return SCIPsetInfinity(set);
13032 }
13033 if( var->data.aggregate.scalar > 0.0 )
13034 {
13035 /* a > 0 -> get upper bound of y */
13036 return var->data.aggregate.scalar * SCIPvarGetUbLP(var->data.aggregate.var, set) + var->data.aggregate.constant;
13037 }
13038 else if( var->data.aggregate.scalar < 0.0 )
13039 {
13040 /* a < 0 -> get lower bound of y */
13041 return var->data.aggregate.scalar * SCIPvarGetLbLP(var->data.aggregate.var, set) + var->data.aggregate.constant;
13042 }
13043 else
13044 {
13045 SCIPerrorMessage("scalar is zero in aggregation\n");
13046 SCIPABORT();
13047 return SCIP_INVALID; /*lint !e527*/
13048 }
13049
13050 case SCIP_VARSTATUS_MULTAGGR:
13051 SCIPerrorMessage("cannot get the bounds of a multi-aggregated variable.\n");
13052 SCIPABORT();
13053 return SCIP_INVALID; /*lint !e527*/
13054
13055 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
13056 assert(var->negatedvar != NULL);
13057 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
13058 assert(var->negatedvar->negatedvar == var);
13059 return var->data.negate.constant - SCIPvarGetLbLP(var->negatedvar, set);
13060
13061 default:
13062 SCIPerrorMessage("unknown variable status\n");
13063 SCIPABORT();
13064 return SCIP_INVALID; /*lint !e527*/
13065 }
13066 }
13067
13068 /** gets primal LP solution value of variable */
13069 SCIP_Real SCIPvarGetLPSol_rec(
13070 SCIP_VAR* var /**< problem variable */
13071 )
13072 {
13073 assert(var != NULL);
13074
13075 switch( SCIPvarGetStatus(var) )
13076 {
13077 case SCIP_VARSTATUS_ORIGINAL:
13078 if( var->data.original.transvar == NULL )
13079 return SCIP_INVALID;
13080 return SCIPvarGetLPSol(var->data.original.transvar);
13081
13082 case SCIP_VARSTATUS_LOOSE:
13083 return SCIPvarGetBestBoundLocal(var);
13084
13085 case SCIP_VARSTATUS_COLUMN:
13086 assert(var->data.col != NULL);
13087 return SCIPcolGetPrimsol(var->data.col);
13088
13089 case SCIP_VARSTATUS_FIXED:
13090 assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
13091 return var->locdom.lb;
13092
13093 case SCIP_VARSTATUS_AGGREGATED:
13094 {
13095 SCIP_Real lpsolval;
13096
13097 assert(!var->donotaggr);
13098 assert(var->data.aggregate.var != NULL);
13099 lpsolval = SCIPvarGetLPSol(var->data.aggregate.var);
13100
13101 /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
13102 * corresponding infinity value instead of performing an arithmetical transformation (compare method
13103 * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
13104 * (or is called by) a public interface method; instead, we only assert that values are finite
13105 * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
13106 * positives and negatives if the parameter <numerics/infinity> is modified by the user
13107 */
13108 assert(lpsolval > -SCIP_DEFAULT_INFINITY);
13109 assert(lpsolval < +SCIP_DEFAULT_INFINITY);
13110 return var->data.aggregate.scalar * lpsolval + var->data.aggregate.constant;
13111 }
13112 case SCIP_VARSTATUS_MULTAGGR:
13113 {
13114 SCIP_Real primsol;
13115 int i;
13116
13117 assert(!var->donotmultaggr);
13118 assert(var->data.multaggr.vars != NULL);
13119 assert(var->data.multaggr.scalars != NULL);
13120 /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
13121 * assert(var->data.multaggr.nvars >= 2);
13122 */
13123 primsol = var->data.multaggr.constant;
13124 for( i = 0; i < var->data.multaggr.nvars; ++i )
13125 primsol += var->data.multaggr.scalars[i] * SCIPvarGetLPSol(var->data.multaggr.vars[i]);
13126 return primsol;
13127 }
13128 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
13129 assert(var->negatedvar != NULL);
13130 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
13131 assert(var->negatedvar->negatedvar == var);
13132 return var->data.negate.constant - SCIPvarGetLPSol(var->negatedvar);
13133
13134 default:
13135 SCIPerrorMessage("unknown variable status\n");
13136 SCIPABORT();
13137 return SCIP_INVALID; /*lint !e527*/
13138 }
13139 }
13140
13141 /** gets primal NLP solution value of variable */
13142 SCIP_Real SCIPvarGetNLPSol_rec(
13143 SCIP_VAR* var /**< problem variable */
13144 )
13145 {
13146 SCIP_Real solval;
13147 int i;
13148
13149 assert(var != NULL);
13150
13151 /* only values for non fixed variables (LOOSE or COLUMN) are stored; others have to be transformed */
13152 switch( SCIPvarGetStatus(var) )
13153 {
13154 case SCIP_VARSTATUS_ORIGINAL:
13155 return SCIPvarGetNLPSol(var->data.original.transvar);
13156
13157 case SCIP_VARSTATUS_LOOSE:
13158 case SCIP_VARSTATUS_COLUMN:
13159 return var->nlpsol;
13160
13161 case SCIP_VARSTATUS_FIXED:
13162 assert(SCIPvarGetLbGlobal(var) == SCIPvarGetUbGlobal(var)); /*lint !e777*/
13163 assert(SCIPvarGetLbLocal(var) == SCIPvarGetUbLocal(var)); /*lint !e777*/
13164 assert(SCIPvarGetLbGlobal(var) == SCIPvarGetLbLocal(var)); /*lint !e777*/
13165 return SCIPvarGetLbGlobal(var);
13166
13167 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c => y = (x-c)/a */
13168 solval = SCIPvarGetNLPSol(var->data.aggregate.var);
13169 return var->data.aggregate.scalar * solval + var->data.aggregate.constant;
13170
13171 case SCIP_VARSTATUS_MULTAGGR:
13172 solval = var->data.multaggr.constant;
13173 for( i = 0; i < var->data.multaggr.nvars; ++i )
13174 solval += var->data.multaggr.scalars[i] * SCIPvarGetNLPSol(var->data.multaggr.vars[i]);
13175 return solval;
13176
13177 case SCIP_VARSTATUS_NEGATED:
13178 solval = SCIPvarGetNLPSol(var->negatedvar);
13179 return var->data.negate.constant - solval;
13180
13181 default:
13182 SCIPerrorMessage("unknown variable status\n");
13183 SCIPABORT();
13184 return SCIP_INVALID; /*lint !e527*/
13185 }
13186 }
13187
13188 /** gets pseudo solution value of variable at current node */
13189 static
13190 SCIP_Real SCIPvarGetPseudoSol_rec(
13191 SCIP_VAR* var /**< problem variable */
13192 )
13193 {
13194 SCIP_Real pseudosol;
13195 int i;
13196
13197 assert(var != NULL);
13198
13199 switch( SCIPvarGetStatus(var) )
13200 {
13201 case SCIP_VARSTATUS_ORIGINAL:
13202 if( var->data.original.transvar == NULL )
13203 return SCIP_INVALID;
13204 return SCIPvarGetPseudoSol(var->data.original.transvar);
13205
13206 case SCIP_VARSTATUS_LOOSE:
13207 case SCIP_VARSTATUS_COLUMN:
13208 return SCIPvarGetBestBoundLocal(var);
13209
13210 case SCIP_VARSTATUS_FIXED:
13211 assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
13212 return var->locdom.lb;
13213
13214 case SCIP_VARSTATUS_AGGREGATED:
13215 {
13216 SCIP_Real pseudosolval;
13217 assert(!var->donotaggr);
13218 assert(var->data.aggregate.var != NULL);
13219 /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
13220 * corresponding infinity value instead of performing an arithmetical transformation (compare method
13221 * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
13222 * (or is called by) a public interface method; instead, we only assert that values are finite
13223 * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
13224 * positives and negatives if the parameter <numerics/infinity> is modified by the user
13225 */
13226 pseudosolval = SCIPvarGetPseudoSol(var->data.aggregate.var);
13227 assert(pseudosolval > -SCIP_DEFAULT_INFINITY);
13228 assert(pseudosolval < +SCIP_DEFAULT_INFINITY);
13229 return var->data.aggregate.scalar * pseudosolval + var->data.aggregate.constant;
13230 }
13231 case SCIP_VARSTATUS_MULTAGGR:
13232 assert(!var->donotmultaggr);
13233 assert(var->data.multaggr.vars != NULL);
13234 assert(var->data.multaggr.scalars != NULL);
13235 /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
13236 * assert(var->data.multaggr.nvars >= 2);
13237 */
13238 pseudosol = var->data.multaggr.constant;
13239 for( i = 0; i < var->data.multaggr.nvars; ++i )
13240 pseudosol += var->data.multaggr.scalars[i] * SCIPvarGetPseudoSol(var->data.multaggr.vars[i]);
13241 return pseudosol;
13242
13243 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
13244 assert(var->negatedvar != NULL);
13245 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
13246 assert(var->negatedvar->negatedvar == var);
13247 return var->data.negate.constant - SCIPvarGetPseudoSol(var->negatedvar);
13248
13249 default:
13250 SCIPerrorMessage("unknown variable status\n");
13251 SCIPABORT();
13252 return SCIP_INVALID; /*lint !e527*/
13253 }
13254 }
13255
13256 /** gets current LP or pseudo solution value of variable */
13257 SCIP_Real SCIPvarGetSol(
13258 SCIP_VAR* var, /**< problem variable */
13259 SCIP_Bool getlpval /**< should the LP solution value be returned? */
13260 )
13261 {
13262 if( getlpval )
13263 return SCIPvarGetLPSol(var);
13264 else
13265 return SCIPvarGetPseudoSol(var);
13266 }
13267
13268 /** remembers the current solution as root solution in the problem variables */
13269 void SCIPvarStoreRootSol(
13270 SCIP_VAR* var, /**< problem variable */
13271 SCIP_Bool roothaslp /**< is the root solution from LP? */
13272 )
13273 {
13274 assert(var != NULL);
13275
13276 var->rootsol = SCIPvarGetSol(var, roothaslp);
13277 }
13278
13279 /** updates the current solution as best root solution of the given variable if it is better */
13280 void SCIPvarUpdateBestRootSol(
13281 SCIP_VAR* var, /**< problem variable */
13282 SCIP_SET* set, /**< global SCIP settings */
13283 SCIP_Real rootsol, /**< root solution value */
13284 SCIP_Real rootredcost, /**< root reduced cost */
13285 SCIP_Real rootlpobjval /**< objective value of the root LP */
13286 )
13287 {
13288 assert(var != NULL);
13289 assert(set != NULL);
13290 assert(var->scip == set->scip);
13291
13292 /* if reduced cost are zero nothing to update */
13293 if( SCIPsetIsDualfeasZero(set, rootredcost) )
13294 return;
13295
13296 /* check if we have already a best combination stored */
13297 if( !SCIPsetIsDualfeasZero(set, var->bestrootredcost) )
13298 {
13299 SCIP_Real currcutoffbound;
13300 SCIP_Real cutoffbound;
13301 SCIP_Real bound;
13302
13303 /* compute the cutoff bound which would improve the corresponding bound with the current stored root solution,
13304 * root reduced cost, and root LP objective value combination
13305 */
13306 if( var->bestrootredcost > 0.0 )
13307 bound = SCIPvarGetUbGlobal(var);
13308 else
13309 bound = SCIPvarGetLbGlobal(var);
13310
13311 currcutoffbound = (bound - var->bestrootsol) * var->bestrootredcost + var->bestrootlpobjval;
13312
13313 /* compute the cutoff bound which would improve the corresponding bound with new root solution, root reduced
13314 * cost, and root LP objective value combination
13315 */
13316 if( rootredcost > 0.0 )
13317 bound = SCIPvarGetUbGlobal(var);
13318 else
13319 bound = SCIPvarGetLbGlobal(var);
13320
13321 cutoffbound = (bound - rootsol) * rootredcost + rootlpobjval;
13322
13323 /* check if an improving root solution, root reduced cost, and root LP objective value is at hand */
13324 if( cutoffbound > currcutoffbound )
13325 {
13326 SCIPsetDebugMsg(set, "-> <%s> update potential cutoff bound <%g> -> <%g>\n",
13327 SCIPvarGetName(var), currcutoffbound, cutoffbound);
13328
13329 var->bestrootsol = rootsol;
13330 var->bestrootredcost = rootredcost;
13331 var->bestrootlpobjval = rootlpobjval;
13332 }
13333 }
13334 else
13335 {
13336 SCIPsetDebugMsg(set, "-> <%s> initialize best root reduced cost information\n", SCIPvarGetName(var));
13337 SCIPsetDebugMsg(set, " -> rootsol <%g>\n", rootsol);
13338 SCIPsetDebugMsg(set, " -> rootredcost <%g>\n", rootredcost);
13339 SCIPsetDebugMsg(set, " -> rootlpobjval <%g>\n", rootlpobjval);
13340
13341 var->bestrootsol = rootsol;
13342 var->bestrootredcost = rootredcost;
13343 var->bestrootlpobjval = rootlpobjval;
13344 }
13345 }
13346
13347 /** returns the solution of the variable in the last root node's relaxation, if the root relaxation is not yet
13348 * completely solved, zero is returned
13349 */
13350 SCIP_Real SCIPvarGetRootSol(
13351 SCIP_VAR* var /**< problem variable */
13352 )
13353 {
13354 SCIP_Real rootsol;
13355 int i;
13356
13357 assert(var != NULL);
13358
13359 switch( SCIPvarGetStatus(var) )
13360 {
13361 case SCIP_VARSTATUS_ORIGINAL:
13362 if( var->data.original.transvar == NULL )
13363 return 0.0;
13364 return SCIPvarGetRootSol(var->data.original.transvar);
13365
13366 case SCIP_VARSTATUS_LOOSE:
13367 case SCIP_VARSTATUS_COLUMN:
13368 return var->rootsol;
13369
13370 case SCIP_VARSTATUS_FIXED:
13371 assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
13372 return var->locdom.lb;
13373
13374 case SCIP_VARSTATUS_AGGREGATED:
13375 assert(!var->donotaggr);
13376 assert(var->data.aggregate.var != NULL);
13377 /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
13378 * corresponding infinity value instead of performing an arithmetical transformation (compare method
13379 * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
13380 * (or is called by) a public interface method; instead, we only assert that values are finite
13381 * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
13382 * positives and negatives if the parameter <numerics/infinity> is modified by the user
13383 */
13384 assert(SCIPvarGetRootSol(var->data.aggregate.var) > -SCIP_DEFAULT_INFINITY);
13385 assert(SCIPvarGetRootSol(var->data.aggregate.var) < +SCIP_DEFAULT_INFINITY);
13386 return var->data.aggregate.scalar * SCIPvarGetRootSol(var->data.aggregate.var) + var->data.aggregate.constant;
13387
13388 case SCIP_VARSTATUS_MULTAGGR:
13389 assert(!var->donotmultaggr);
13390 assert(var->data.multaggr.vars != NULL);
13391 assert(var->data.multaggr.scalars != NULL);
13392 /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
13393 * assert(var->data.multaggr.nvars >= 2);
13394 */
13395 rootsol = var->data.multaggr.constant;
13396 for( i = 0; i < var->data.multaggr.nvars; ++i )
13397 rootsol += var->data.multaggr.scalars[i] * SCIPvarGetRootSol(var->data.multaggr.vars[i]);
13398 return rootsol;
13399
13400 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
13401 assert(var->negatedvar != NULL);
13402 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
13403 assert(var->negatedvar->negatedvar == var);
13404 return var->data.negate.constant - SCIPvarGetRootSol(var->negatedvar);
13405
13406 default:
13407 SCIPerrorMessage("unknown variable status\n");
13408 SCIPABORT();
13409 return SCIP_INVALID; /*lint !e527*/
13410 }
13411 }
13412
13413 /** returns for given variable the reduced cost */
13414 static
13415 SCIP_Real getImplVarRedcost(
13416 SCIP_VAR* var, /**< problem variable */
13417 SCIP_SET* set, /**< global SCIP settings */
13418 SCIP_Bool varfixing, /**< FALSE if for x == 0, TRUE for x == 1 */
13419 SCIP_STAT* stat, /**< problem statistics */
13420 SCIP_LP* lp /**< current LP data */
13421 )
13422 {
13423 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
13424 {
13425 SCIP_COL* col;
13426 SCIP_Real primsol;
13427 SCIP_BASESTAT basestat;
13428 SCIP_Bool lpissolbasic;
13429
13430 col = SCIPvarGetCol(var);
13431 assert(col != NULL);
13432
13433 basestat = SCIPcolGetBasisStatus(col);
13434 lpissolbasic = SCIPlpIsSolBasic(lp);
13435 primsol = SCIPcolGetPrimsol(col);
13436
13437 if( (lpissolbasic && (basestat == SCIP_BASESTAT_LOWER || basestat == SCIP_BASESTAT_UPPER)) ||
13438 (!lpissolbasic && (SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), primsol) || SCIPsetIsFeasEQ(set, SCIPvarGetUbLocal(var), primsol))) )
13439 {
13440 SCIP_Real redcost = SCIPcolGetRedcost(col, stat, lp);
13441
13442 assert(((!lpissolbasic && SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), primsol)) ||
13443 (lpissolbasic && basestat == SCIP_BASESTAT_LOWER)) ? (!SCIPsetIsDualfeasNegative(set, redcost) ||
13444 SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var))) : TRUE);
13445 assert(((!lpissolbasic && SCIPsetIsFeasEQ(set, SCIPvarGetUbLocal(var), primsol)) ||
13446 (lpissolbasic && basestat == SCIP_BASESTAT_UPPER)) ? (!SCIPsetIsDualfeasPositive(set, redcost) ||
13447 SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var))) : TRUE);
13448
13449 if( (varfixing && ((lpissolbasic && basestat == SCIP_BASESTAT_LOWER) ||
13450 (!lpissolbasic && SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), primsol)))) ||
13451 (!varfixing && ((lpissolbasic && basestat == SCIP_BASESTAT_UPPER) ||
13452 (!lpissolbasic && SCIPsetIsFeasEQ(set, SCIPvarGetUbLocal(var), primsol)))) )
13453 return redcost;
13454 else
13455 return 0.0;
13456 }
13457
13458 return 0.0;
13459 }
13460
13461 return 0.0;
13462 }
13463
13464 #define MAX_CLIQUELENGTH 50
13465 /** returns for the given binary variable the reduced cost which are given by the variable itself and its implication if
13466 * the binary variable is fixed to the given value
13467 */
13468 SCIP_Real SCIPvarGetImplRedcost(
13469 SCIP_VAR* var, /**< problem variable */
13470 SCIP_SET* set, /**< global SCIP settings */
13471 SCIP_Bool varfixing, /**< FALSE if for x == 0, TRUE for x == 1 */
13472 SCIP_STAT* stat, /**< problem statistics */
13473 SCIP_PROB* prob, /**< transformed problem, or NULL */
13474 SCIP_LP* lp /**< current LP data */
13475 )
13476 {
13477 SCIP_Real implredcost;
13478 int ncliques;
13479 int nvars;
13480
13481 assert(SCIPvarIsBinary(var));
13482 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
13483
13484 /* get reduced cost of given variable */
13485 implredcost = getImplVarRedcost(var, set, varfixing, stat, lp);
13486
13487 #ifdef SCIP_MORE_DEBUG
13488 SCIPsetDebugMsg(set, "variable <%s> itself has reduced cost of %g\n", SCIPvarGetName(var), implredcost);
13489 #endif
13490
13491 /* the following algorithm is expensive */
13492 ncliques = SCIPvarGetNCliques(var, varfixing);
13493
13494 if( ncliques > 0 )
13495 {
13496 SCIP_CLIQUE** cliques;
13497 SCIP_CLIQUE* clique;
13498 SCIP_VAR** clqvars;
13499 SCIP_VAR** probvars;
13500 SCIP_VAR* clqvar;
13501 SCIP_Bool* clqvalues;
13502 int* entries;
13503 int* ids;
13504 SCIP_Real redcost;
13505 SCIP_Bool cleanedup;
13506 int nclqvars;
13507 int nentries;
13508 int nids;
13509 int id;
13510 int c;
13511 int v;
13512
13513 assert(prob != NULL);
13514 assert(SCIPprobIsTransformed(prob));
13515
13516 nentries = SCIPprobGetNVars(prob) - SCIPprobGetNContVars(prob) + 1;
13517
13518 SCIP_CALL_ABORT( SCIPsetAllocBufferArray(set, &ids, nentries) );
13519 nids = 0;
13520 SCIP_CALL_ABORT( SCIPsetAllocCleanBufferArray(set, &entries, nentries) );
13521
13522 cliques = SCIPvarGetCliques(var, varfixing);
13523 assert(cliques != NULL);
13524
13525 for( c = ncliques - 1; c >= 0; --c )
13526 {
13527 clique = cliques[c];
13528 assert(clique != NULL);
13529 nclqvars = SCIPcliqueGetNVars(clique);
13530 assert(nclqvars > 0);
13531
13532 if( nclqvars > MAX_CLIQUELENGTH )
13533 continue;
13534
13535 clqvars = SCIPcliqueGetVars(clique);
13536 clqvalues = SCIPcliqueGetValues(clique);
13537 assert(clqvars != NULL);
13538 assert(clqvalues != NULL);
13539
13540 cleanedup = SCIPcliqueIsCleanedUp(clique);
13541
13542 for( v = nclqvars - 1; v >= 0; --v )
13543 {
13544 clqvar = clqvars[v];
13545 assert(clqvar != NULL);
13546
13547 /* ignore binary variable which are fixed */
13548 if( clqvar != var && (cleanedup || SCIPvarIsActive(clqvar)) &&
13549 (SCIPvarGetLbLocal(clqvar) < 0.5 && SCIPvarGetUbLocal(clqvar) > 0.5) )
13550 {
13551 int probindex = SCIPvarGetProbindex(clqvar) + 1;
13552 assert(0 < probindex && probindex < nentries);
13553
13554 #if 0
13555 /* check that the variable was not yet visited or does not appear with two contradicting implications, ->
13556 * can appear since there is no guarantee that all these infeasible bounds were found
13557 */
13558 assert(!entries[probindex] || entries[probindex] == (clqvalues[v] ? probindex : -probindex));
13559 #endif
13560 if( entries[probindex] == 0 )
13561 {
13562 ids[nids] = probindex;
13563 ++nids;
13564
13565 /* mark variable as visited */
13566 entries[probindex] = (clqvalues[v] ? probindex : -probindex);
13567 }
13568 }
13569 }
13570 }
13571
13572 probvars = SCIPprobGetVars(prob);
13573 assert(probvars != NULL);
13574
13575 /* add all implied reduced cost */
13576 for( v = nids - 1; v >= 0; --v )
13577 {
13578 id = ids[v];
13579 assert(0 < id && id < nentries);
13580 assert(entries[id] != 0);
13581 assert(probvars[id - 1] != NULL);
13582 assert(SCIPvarIsActive(probvars[id - 1]));
13583 assert(SCIPvarIsBinary(probvars[id - 1]));
13584 assert(SCIPvarGetLbLocal(probvars[id - 1]) < 0.5 && SCIPvarGetUbLocal(probvars[id - 1]) > 0.5);
13585
13586 if( (entries[id] > 0) != varfixing )
13587 redcost = getImplVarRedcost(probvars[id - 1], set, (entries[id] < 0), stat, lp);
13588 else
13589 redcost = -getImplVarRedcost(probvars[id - 1], set, (entries[id] < 0), stat, lp);
13590
13591 if( (varfixing && SCIPsetIsDualfeasPositive(set, redcost)) || (!varfixing && SCIPsetIsDualfeasNegative(set, redcost)) )
13592 implredcost += redcost;
13593
13594 /* reset entries clear buffer array */
13595 entries[id] = 0;
13596 }
13597
13598 SCIPsetFreeCleanBufferArray(set, &entries);
13599 SCIPsetFreeBufferArray(set, &ids);
13600 }
13601
13602 #ifdef SCIP_MORE_DEBUG
13603 SCIPsetDebugMsg(set, "variable <%s> incl. cliques (%d) has implied reduced cost of %g\n", SCIPvarGetName(var), ncliques,
13604 implredcost);
13605 #endif
13606
13607 /* collect non-binary implication information */
13608 nvars = SCIPimplicsGetNImpls(var->implics, varfixing);
13609
13610 if( nvars > 0 )
13611 {
13612 SCIP_VAR** vars;
13613 SCIP_VAR* implvar;
13614 SCIP_COL* col;
13615 SCIP_Real* bounds;
13616 SCIP_BOUNDTYPE* boundtypes;
13617 SCIP_Real redcost;
13618 SCIP_Real lb;
13619 SCIP_Real ub;
13620 SCIP_Bool lpissolbasic;
13621 int v;
13622
13623 vars = SCIPimplicsGetVars(var->implics, varfixing);
13624 boundtypes = SCIPimplicsGetTypes(var->implics, varfixing);
13625 bounds = SCIPimplicsGetBounds(var->implics, varfixing);
13626 lpissolbasic = SCIPlpIsSolBasic(lp);
13627
13628 for( v = nvars - 1; v >= 0; --v )
13629 {
13630 implvar = vars[v];
13631 assert(implvar != NULL);
13632
13633 lb = SCIPvarGetLbLocal(implvar);
13634 ub = SCIPvarGetUbLocal(implvar);
13635
13636 /* ignore binary variable which are fixed or not of column status */
13637 if( SCIPvarGetStatus(implvar) != SCIP_VARSTATUS_COLUMN || SCIPsetIsFeasEQ(set, lb, ub) )
13638 continue;
13639
13640 col = SCIPvarGetCol(implvar);
13641 assert(col != NULL);
13642 redcost = 0.0;
13643
13644 /* solved lp with basis information or not? */
13645 if( lpissolbasic )
13646 {
13647 SCIP_BASESTAT basestat = SCIPcolGetBasisStatus(col);
13648
13649 /* check if the implication is not not yet applied */
13650 if( basestat == SCIP_BASESTAT_LOWER && boundtypes[v] == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasGT(set, bounds[v], lb) )
13651 {
13652 redcost = SCIPcolGetRedcost(col, stat, lp);
13653 assert(!SCIPsetIsDualfeasNegative(set, redcost));
13654
13655 if( !varfixing )
13656 redcost *= (lb - bounds[v]);
13657 else
13658 redcost *= (bounds[v] - lb);
13659 }
13660 else if( basestat == SCIP_BASESTAT_UPPER && boundtypes[v] == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasLT(set, bounds[v], ub) )
13661 {
13662 redcost = SCIPcolGetRedcost(col, stat, lp);
13663 assert(!SCIPsetIsDualfeasPositive(set, redcost));
13664
13665 if( varfixing )
13666 redcost *= (bounds[v] - ub);
13667 else
13668 redcost *= (ub - bounds[v]);
13669 }
13670 }
13671 else
13672 {
13673 SCIP_Real primsol = SCIPcolGetPrimsol(col);
13674
13675 /* check if the implication is not not yet applied */
13676 if( boundtypes[v] == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasEQ(set, lb, primsol) && SCIPsetIsFeasGT(set, bounds[v], lb) )
13677 {
13678 redcost = SCIPcolGetRedcost(col, stat, lp);
13679 assert(!SCIPsetIsDualfeasNegative(set, redcost));
13680
13681 if( varfixing )
13682 redcost *= (lb - bounds[v]);
13683 else
13684 redcost *= (bounds[v] - lb);
13685 }
13686 else if( boundtypes[v] == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasEQ(set, ub, primsol) && SCIPsetIsFeasLT(set, bounds[v], ub) )
13687 {
13688 redcost = SCIPcolGetRedcost(col, stat, lp);
13689 assert(!SCIPsetIsDualfeasPositive(set, redcost));
13690
13691 if( varfixing )
13692 redcost *= (bounds[v] - ub);
13693 else
13694 redcost *= (ub - bounds[v]);
13695 }
13696 }
13697
13698 /* improve implied reduced cost */
13699 if( (varfixing && SCIPsetIsDualfeasPositive(set, redcost)) || (!varfixing && SCIPsetIsDualfeasNegative(set, redcost)) )
13700 implredcost += redcost;
13701 }
13702 }
13703
13704 #ifdef SCIP_MORE_DEBUG
13705 SCIPsetDebugMsg(set, "variable <%s> incl. cliques (%d) and implications (%d) has implied reduced cost of %g\n",
13706 SCIPvarGetName(var), ncliques, nvars, implredcost);
13707 #endif
13708
13709 return implredcost;
13710 }
13711
13712 /** returns the best solution (w.r.t. root reduced cost propagation) of the variable in the root node's relaxation, if
13713 * the root relaxation is not yet completely solved, zero is returned
13714 */
13715 SCIP_Real SCIPvarGetBestRootSol(
13716 SCIP_VAR* var /**< problem variable */
13717 )
13718 {
13719 SCIP_Real rootsol;
13720 int i;
13721
13722 assert(var != NULL);
13723
13724 switch( SCIPvarGetStatus(var) )
13725 {
13726 case SCIP_VARSTATUS_ORIGINAL:
13727 if( var->data.original.transvar == NULL )
13728 return 0.0;
13729 return SCIPvarGetBestRootSol(var->data.original.transvar);
13730
13731 case SCIP_VARSTATUS_LOOSE:
13732 case SCIP_VARSTATUS_COLUMN:
13733 return var->bestrootsol;
13734
13735 case SCIP_VARSTATUS_FIXED:
13736 assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
13737 return var->locdom.lb;
13738
13739 case SCIP_VARSTATUS_AGGREGATED:
13740 assert(!var->donotaggr);
13741 assert(var->data.aggregate.var != NULL);
13742 /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
13743 * corresponding infinity value instead of performing an arithmetical transformation (compare method
13744 * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
13745 * (or is called by) a public interface method; instead, we only assert that values are finite
13746 * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
13747 * positives and negatives if the parameter <numerics/infinity> is modified by the user
13748 */
13749 assert(SCIPvarGetBestRootSol(var->data.aggregate.var) > -SCIP_DEFAULT_INFINITY);
13750 assert(SCIPvarGetBestRootSol(var->data.aggregate.var) < +SCIP_DEFAULT_INFINITY);
13751 return var->data.aggregate.scalar * SCIPvarGetBestRootSol(var->data.aggregate.var) + var->data.aggregate.constant;
13752
13753 case SCIP_VARSTATUS_MULTAGGR:
13754 assert(!var->donotmultaggr);
13755 assert(var->data.multaggr.vars != NULL);
13756 assert(var->data.multaggr.scalars != NULL);
13757 /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
13758 * assert(var->data.multaggr.nvars >= 2);
13759 */
13760 rootsol = var->data.multaggr.constant;
13761 for( i = 0; i < var->data.multaggr.nvars; ++i )
13762 rootsol += var->data.multaggr.scalars[i] * SCIPvarGetBestRootSol(var->data.multaggr.vars[i]);
13763 return rootsol;
13764
13765 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
13766 assert(var->negatedvar != NULL);
13767 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
13768 assert(var->negatedvar->negatedvar == var);
13769 return var->data.negate.constant - SCIPvarGetBestRootSol(var->negatedvar);
13770
13771 default:
13772 SCIPerrorMessage("unknown variable status\n");
13773 SCIPABORT();
13774 return 0.0; /*lint !e527*/
13775 }
13776 }
13777
13778 /** returns the best reduced costs (w.r.t. root reduced cost propagation) of the variable in the root node's relaxation,
13779 * if the root relaxation is not yet completely solved, or the variable was no column of the root LP, SCIP_INVALID is
13780 * returned
13781 */
13782 SCIP_Real SCIPvarGetBestRootRedcost(
13783 SCIP_VAR* var /**< problem variable */
13784 )
13785 {
13786 assert(var != NULL);
13787
13788 switch( SCIPvarGetStatus(var) )
13789 {
13790 case SCIP_VARSTATUS_ORIGINAL:
13791 if( var->data.original.transvar == NULL )
13792 return SCIP_INVALID;
13793 return SCIPvarGetBestRootRedcost(var->data.original.transvar);
13794
13795 case SCIP_VARSTATUS_LOOSE:
13796 case SCIP_VARSTATUS_COLUMN:
13797 return var->bestrootredcost;
13798
13799 case SCIP_VARSTATUS_FIXED:
13800 case SCIP_VARSTATUS_AGGREGATED:
13801 case SCIP_VARSTATUS_MULTAGGR:
13802 case SCIP_VARSTATUS_NEGATED:
13803 return 0.0;
13804
13805 default:
13806 SCIPerrorMessage("unknown variable status\n");
13807 SCIPABORT();
13808 return 0.0; /*lint !e527*/
13809 }
13810 }
13811
13812 /** returns the best objective value (w.r.t. root reduced cost propagation) of the root LP which belongs the root
13813 * reduced cost which is accessible via SCIPvarGetRootRedcost() or the variable was no column of the root LP,
13814 * SCIP_INVALID is returned
13815 */
13816 SCIP_Real SCIPvarGetBestRootLPObjval(
13817 SCIP_VAR* var /**< problem variable */
13818 )
13819 {
13820 assert(var != NULL);
13821
13822 switch( SCIPvarGetStatus(var) )
13823 {
13824 case SCIP_VARSTATUS_ORIGINAL:
13825 if( var->data.original.transvar == NULL )
13826 return SCIP_INVALID;
13827 return SCIPvarGetBestRootLPObjval(var->data.original.transvar);
13828
13829 case SCIP_VARSTATUS_LOOSE:
13830 case SCIP_VARSTATUS_COLUMN:
13831 return var->bestrootlpobjval;
13832
13833 case SCIP_VARSTATUS_FIXED:
13834 case SCIP_VARSTATUS_AGGREGATED:
13835 case SCIP_VARSTATUS_MULTAGGR:
13836 case SCIP_VARSTATUS_NEGATED:
13837 return SCIP_INVALID;
13838
13839 default:
13840 SCIPerrorMessage("unknown variable status\n");
13841 SCIPABORT();
13842 return SCIP_INVALID; /*lint !e527*/
13843 }
13844 }
13845
13846 /** set the given solution as the best root solution w.r.t. root reduced cost propagation in the variables */
13847 void SCIPvarSetBestRootSol(
13848 SCIP_VAR* var, /**< problem variable */
13849 SCIP_Real rootsol, /**< root solution value */
13850 SCIP_Real rootredcost, /**< root reduced cost */
13851 SCIP_Real rootlpobjval /**< objective value of the root LP */
13852 )
13853 {
13854 assert(var != NULL);
13855
13856 var->bestrootsol = rootsol;
13857 var->bestrootredcost = rootredcost;
13858 var->bestrootlpobjval = rootlpobjval;
13859 }
13860
13861 /** stores the solution value as relaxation solution in the problem variable */
13862 SCIP_RETCODE SCIPvarSetRelaxSol(
13863 SCIP_VAR* var, /**< problem variable */
13864 SCIP_SET* set, /**< global SCIP settings */
13865 SCIP_RELAXATION* relaxation, /**< global relaxation data */
13866 SCIP_Real solval, /**< solution value in the current relaxation solution */
13867 SCIP_Bool updateobj /**< should the objective value be updated? */
13868 )
13869 {
13870 assert(var != NULL);
13871 assert(relaxation != NULL);
13872 assert(set != NULL);
13873 assert(var->scip == set->scip);
13874
13875 /* we want to store only values for non fixed variables (LOOSE or COLUMN); others have to be transformed */
13876 switch( SCIPvarGetStatus(var) )
13877 {
13878 case SCIP_VARSTATUS_ORIGINAL:
13879 SCIP_CALL( SCIPvarSetRelaxSol(var->data.original.transvar, set, relaxation, solval, updateobj) );
13880 break;
13881
13882 case SCIP_VARSTATUS_LOOSE:
13883 case SCIP_VARSTATUS_COLUMN:
13884 if( updateobj )
13885 SCIPrelaxationSolObjAdd(relaxation, var->obj * (solval - var->relaxsol));
13886 var->relaxsol = solval;
13887 break;
13888
13889 case SCIP_VARSTATUS_FIXED:
13890 if( !SCIPsetIsEQ(set, solval, var->glbdom.lb) )
13891 {
13892 SCIPerrorMessage("cannot set relaxation solution value for variable <%s> fixed to %.15g to different value %.15g\n",
13893 SCIPvarGetName(var), var->glbdom.lb, solval);
13894 return SCIP_INVALIDDATA;
13895 }
13896 break;
13897
13898 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c => y = (x-c)/a */
13899 assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
13900 SCIP_CALL( SCIPvarSetRelaxSol(var->data.aggregate.var, set, relaxation,
13901 (solval - var->data.aggregate.constant)/var->data.aggregate.scalar, updateobj) );
13902 break;
13903 case SCIP_VARSTATUS_MULTAGGR:
13904 SCIPerrorMessage("cannot set solution value for multiple aggregated variable\n");
13905 return SCIP_INVALIDDATA;
13906
13907 case SCIP_VARSTATUS_NEGATED:
13908 SCIP_CALL( SCIPvarSetRelaxSol(var->negatedvar, set, relaxation, var->data.negate.constant - solval, updateobj) );
13909 break;
13910
13911 default:
13912 SCIPerrorMessage("unknown variable status\n");
13913 return SCIP_INVALIDDATA;
13914 }
13915
13916 return SCIP_OKAY;
13917 }
13918
13919 /** returns the solution value of the problem variable in the relaxation solution
13920 *
13921 * @todo Inline this function - similar to SCIPvarGetLPSol_rec.
13922 */
13923 SCIP_Real SCIPvarGetRelaxSol(
13924 SCIP_VAR* var, /**< problem variable */
13925 SCIP_SET* set /**< global SCIP settings */
13926 )
13927 {
13928 SCIP_Real solvalsum;
13929 SCIP_Real solval;
13930 int i;
13931
13932 assert(var != NULL);
13933 assert(set != NULL);
13934 assert(var->scip == set->scip);
13935
13936 /* only values for non fixed variables (LOOSE or COLUMN) are stored; others have to be transformed */
13937 switch( SCIPvarGetStatus(var) )
13938 {
13939 case SCIP_VARSTATUS_ORIGINAL:
13940 return SCIPvarGetRelaxSol(var->data.original.transvar, set);
13941
13942 case SCIP_VARSTATUS_LOOSE:
13943 case SCIP_VARSTATUS_COLUMN:
13944 return var->relaxsol;
13945
13946 case SCIP_VARSTATUS_FIXED:
13947 assert(SCIPvarGetLbGlobal(var) == SCIPvarGetUbGlobal(var)); /*lint !e777*/
13948 assert(SCIPvarGetLbLocal(var) == SCIPvarGetUbLocal(var)); /*lint !e777*/
13949 assert(SCIPvarGetLbGlobal(var) == SCIPvarGetLbLocal(var)); /*lint !e777*/
13950 return SCIPvarGetLbGlobal(var);
13951
13952 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c => y = (x-c)/a */
13953 solval = SCIPvarGetRelaxSol(var->data.aggregate.var, set);
13954 if( SCIPsetIsInfinity(set, solval) || SCIPsetIsInfinity(set, -solval) )
13955 {
13956 if( var->data.aggregate.scalar * solval > 0.0 )
13957 return SCIPsetInfinity(set);
13958 if( var->data.aggregate.scalar * solval < 0.0 )
13959 return -SCIPsetInfinity(set);
13960 }
13961 return var->data.aggregate.scalar * solval + var->data.aggregate.constant;
13962
13963 case SCIP_VARSTATUS_MULTAGGR:
13964 solvalsum = var->data.multaggr.constant;
13965 for( i = 0; i < var->data.multaggr.nvars; ++i )
13966 {
13967 solval = SCIPvarGetRelaxSol(var->data.multaggr.vars[i], set);
13968 if( SCIPsetIsInfinity(set, solval) || SCIPsetIsInfinity(set, -solval) )
13969 {
13970 if( var->data.multaggr.scalars[i] * solval > 0.0 )
13971 return SCIPsetInfinity(set);
13972 if( var->data.multaggr.scalars[i] * solval < 0.0 )
13973 return -SCIPsetInfinity(set);
13974 }
13975 solvalsum += var->data.multaggr.scalars[i] * solval;
13976 }
13977 return solvalsum;
13978
13979 case SCIP_VARSTATUS_NEGATED:
13980 solval = SCIPvarGetRelaxSol(var->negatedvar, set);
13981 if( SCIPsetIsInfinity(set, solval) )
13982 return -SCIPsetInfinity(set);
13983 if( SCIPsetIsInfinity(set, -solval) )
13984 return SCIPsetInfinity(set);
13985 return var->data.negate.constant - solval;
13986
13987 default:
13988 SCIPerrorMessage("unknown variable status\n");
13989 SCIPABORT();
13990 return SCIP_INVALID; /*lint !e527*/
13991 }
13992 }
13993
13994 /** returns the solution value of the transformed problem variable in the relaxation solution */
13995 SCIP_Real SCIPvarGetRelaxSolTransVar(
13996 SCIP_VAR* var /**< problem variable */
13997 )
13998 {
13999 assert(var != NULL);
14000 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
14001
14002 return var->relaxsol;
14003 }
14004
14005 /** stores the solution value as NLP solution in the problem variable */
14006 SCIP_RETCODE SCIPvarSetNLPSol(
14007 SCIP_VAR* var, /**< problem variable */
14008 SCIP_SET* set, /**< global SCIP settings */
14009 SCIP_Real solval /**< solution value in the current NLP solution */
14010 )
14011 {
14012 assert(var != NULL);
14013 assert(set != NULL);
14014 assert(var->scip == set->scip);
14015
14016 /* we want to store only values for non fixed variables (LOOSE or COLUMN); others have to be transformed */
14017 switch( SCIPvarGetStatus(var) )
14018 {
14019 case SCIP_VARSTATUS_ORIGINAL:
14020 SCIP_CALL( SCIPvarSetNLPSol(var->data.original.transvar, set, solval) );
14021 break;
14022
14023 case SCIP_VARSTATUS_LOOSE:
14024 case SCIP_VARSTATUS_COLUMN:
14025 var->nlpsol = solval;
14026 break;
14027
14028 case SCIP_VARSTATUS_FIXED:
14029 if( !SCIPsetIsEQ(set, solval, var->glbdom.lb) )
14030 {
14031 SCIPerrorMessage("cannot set NLP solution value for variable <%s> fixed to %.15g to different value %.15g\n",
14032 SCIPvarGetName(var), var->glbdom.lb, solval);
14033 SCIPABORT();
14034 return SCIP_INVALIDCALL; /*lint !e527*/
14035 }
14036 break;
14037
14038 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c => y = (x-c)/a */
14039 assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
14040 SCIP_CALL( SCIPvarSetNLPSol(var->data.aggregate.var, set, (solval - var->data.aggregate.constant)/var->data.aggregate.scalar) );
14041 break;
14042
14043 case SCIP_VARSTATUS_MULTAGGR:
14044 SCIPerrorMessage("cannot set solution value for multiple aggregated variable\n");
14045 SCIPABORT();
14046 return SCIP_INVALIDCALL; /*lint !e527*/
14047
14048 case SCIP_VARSTATUS_NEGATED:
14049 SCIP_CALL( SCIPvarSetNLPSol(var->negatedvar, set, var->data.negate.constant - solval) );
14050 break;
14051
14052 default:
14053 SCIPerrorMessage("unknown variable status\n");
14054 SCIPABORT();
14055 return SCIP_ERROR; /*lint !e527*/
14056 }
14057
14058 return SCIP_OKAY;
14059 }
14060
14061 /** returns a weighted average solution value of the variable in all feasible primal solutions found so far */
14062 SCIP_Real SCIPvarGetAvgSol(
14063 SCIP_VAR* var /**< problem variable */
14064 )
14065 {
14066 SCIP_Real avgsol;
14067 int i;
14068
14069 assert(var != NULL);
14070
14071 switch( SCIPvarGetStatus(var) )
14072 {
14073 case SCIP_VARSTATUS_ORIGINAL:
14074 if( var->data.original.transvar == NULL )
14075 return 0.0;
14076 return SCIPvarGetAvgSol(var->data.original.transvar);
14077
14078 case SCIP_VARSTATUS_LOOSE:
14079 case SCIP_VARSTATUS_COLUMN:
14080 avgsol = var->primsolavg;
14081 avgsol = MAX(avgsol, var->glbdom.lb);
14082 avgsol = MIN(avgsol, var->glbdom.ub);
14083 return avgsol;
14084
14085 case SCIP_VARSTATUS_FIXED:
14086 assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
14087 return var->locdom.lb;
14088
14089 case SCIP_VARSTATUS_AGGREGATED:
14090 assert(!var->donotaggr);
14091 assert(var->data.aggregate.var != NULL);
14092 return var->data.aggregate.scalar * SCIPvarGetAvgSol(var->data.aggregate.var)
14093 + var->data.aggregate.constant;
14094
14095 case SCIP_VARSTATUS_MULTAGGR:
14096 assert(!var->donotmultaggr);
14097 assert(var->data.multaggr.vars != NULL);
14098 assert(var->data.multaggr.scalars != NULL);
14099 /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
14100 * assert(var->data.multaggr.nvars >= 2);
14101 */
14102 avgsol = var->data.multaggr.constant;
14103 for( i = 0; i < var->data.multaggr.nvars; ++i )
14104 avgsol += var->data.multaggr.scalars[i] * SCIPvarGetAvgSol(var->data.multaggr.vars[i]);
14105 return avgsol;
14106
14107 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
14108 assert(var->negatedvar != NULL);
14109 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
14110 assert(var->negatedvar->negatedvar == var);
14111 return var->data.negate.constant - SCIPvarGetAvgSol(var->negatedvar);
14112
14113 default:
14114 SCIPerrorMessage("unknown variable status\n");
14115 SCIPABORT();
14116 return 0.0; /*lint !e527*/
14117 }
14118 }
14119
14120 /** returns solution value and index of variable lower bound that is closest to the variable's value in the given primal solution
14121 * or current LP solution if no primal solution is given; returns an index of -1 if no variable lower bound is available
14122 */
14123 void SCIPvarGetClosestVlb(
14124 SCIP_VAR* var, /**< active problem variable */
14125 SCIP_SOL* sol, /**< primal solution, or NULL for LP solution */
14126 SCIP_SET* set, /**< global SCIP settings */
14127 SCIP_STAT* stat, /**< problem statistics */
14128 SCIP_Real* closestvlb, /**< pointer to store the value of the closest variable lower bound */
14129 int* closestvlbidx /**< pointer to store the index of the closest variable lower bound */
14130 )
14131 {
14132 int nvlbs;
14133
14134 assert(var != NULL);
14135 assert(stat != NULL);
14136 assert(set != NULL);
14137 assert(var->scip == set->scip);
14138 assert(closestvlb != NULL);
14139 assert(closestvlbidx != NULL);
14140
14141 *closestvlbidx = -1;
14142 *closestvlb = SCIP_REAL_MIN;
14143
14144 nvlbs = SCIPvarGetNVlbs(var);
14145 if( nvlbs > 0 )
14146 {
14147 SCIP_VAR** vlbvars;
14148 SCIP_Real* vlbcoefs;
14149 SCIP_Real* vlbconsts;
14150 int i;
14151
14152 vlbvars = SCIPvarGetVlbVars(var);
14153 vlbcoefs = SCIPvarGetVlbCoefs(var);
14154 vlbconsts = SCIPvarGetVlbConstants(var);
14155
14156 /* check for cached values */
14157 if( var->closestvblpcount == stat->lpcount && var->closestvlbidx != -1 && sol == NULL)
14158 {
14159 i = var->closestvlbidx;
14160 assert(0 <= i && i < nvlbs);
14161 assert(SCIPvarIsActive(vlbvars[i]));
14162 *closestvlbidx = i;
14163 *closestvlb = vlbcoefs[i] * SCIPvarGetLPSol(vlbvars[i]) + vlbconsts[i];
14164 }
14165 else
14166 {
14167 /* search best VUB */
14168 for( i = 0; i < nvlbs; i++ )
14169 {
14170 if( SCIPvarIsActive(vlbvars[i]) )
14171 {
14172 SCIP_Real vlbsol;
14173
14174 vlbsol = vlbcoefs[i] * (sol == NULL ? SCIPvarGetLPSol(vlbvars[i]) : SCIPsolGetVal(sol, set, stat, vlbvars[i])) + vlbconsts[i];
14175 if( vlbsol > *closestvlb )
14176 {
14177 *closestvlb = vlbsol;
14178 *closestvlbidx = i;
14179 }
14180 }
14181 }
14182
14183 if( sol == NULL )
14184 {
14185 /* update cached value */
14186 if( var->closestvblpcount != stat->lpcount )
14187 var->closestvubidx = -1;
14188 var->closestvlbidx = *closestvlbidx;
14189 var->closestvblpcount = stat->lpcount;
14190 }
14191 }
14192 }
14193 }
14194
14195 /** returns solution value and index of variable upper bound that is closest to the variable's value in the given primal solution;
14196 * or current LP solution if no primal solution is given; returns an index of -1 if no variable upper bound is available
14197 */
14198 void SCIPvarGetClosestVub(
14199 SCIP_VAR* var, /**< active problem variable */
14200 SCIP_SOL* sol, /**< primal solution, or NULL for LP solution */
14201 SCIP_SET* set, /**< global SCIP settings */
14202 SCIP_STAT* stat, /**< problem statistics */
14203 SCIP_Real* closestvub, /**< pointer to store the value of the closest variable upper bound */
14204 int* closestvubidx /**< pointer to store the index of the closest variable upper bound */
14205 )
14206 {
14207 int nvubs;
14208
14209 assert(var != NULL);
14210 assert(set != NULL);
14211 assert(var->scip == set->scip);
14212 assert(closestvub != NULL);
14213 assert(closestvubidx != NULL);
14214
14215 *closestvubidx = -1;
14216 *closestvub = SCIP_REAL_MAX;
14217
14218 nvubs = SCIPvarGetNVubs(var);
14219 if( nvubs > 0 )
14220 {
14221 SCIP_VAR** vubvars;
14222 SCIP_Real* vubcoefs;
14223 SCIP_Real* vubconsts;
14224 int i;
14225
14226 vubvars = SCIPvarGetVubVars(var);
14227 vubcoefs = SCIPvarGetVubCoefs(var);
14228 vubconsts = SCIPvarGetVubConstants(var);
14229
14230 /* check for cached values */
14231 if( var->closestvblpcount == stat->lpcount && var->closestvubidx != -1 && sol == NULL)
14232 {
14233 i = var->closestvubidx;
14234 assert(0 <= i && i < nvubs);
14235 assert(SCIPvarIsActive(vubvars[i]));
14236 *closestvubidx = i;
14237 *closestvub = vubcoefs[i] * SCIPvarGetLPSol(vubvars[i]) + vubconsts[i];
14238 }
14239 else
14240 {
14241 /* search best VUB */
14242 for( i = 0; i < nvubs; i++ )
14243 {
14244 if( SCIPvarIsActive(vubvars[i]) )
14245 {
14246 SCIP_Real vubsol;
14247
14248 vubsol = vubcoefs[i] * (sol == NULL ? SCIPvarGetLPSol(vubvars[i]) : SCIPsolGetVal(sol, set, stat, vubvars[i])) + vubconsts[i];
14249 if( vubsol < *closestvub )
14250 {
14251 *closestvub = vubsol;
14252 *closestvubidx = i;
14253 }
14254 }
14255 }
14256
14257 if( sol == NULL )
14258 {
14259 /* update cached value */
14260 if( var->closestvblpcount != stat->lpcount )
14261 var->closestvlbidx = -1;
14262 var->closestvubidx = *closestvubidx;
14263 var->closestvblpcount = stat->lpcount;
14264 }
14265 }
14266 }
14267 }
14268
14269 /** resolves variable to columns and adds them with the coefficient to the row */
14270 SCIP_RETCODE SCIPvarAddToRow(
14271 SCIP_VAR* var, /**< problem variable */
14272 BMS_BLKMEM* blkmem, /**< block memory */
14273 SCIP_SET* set, /**< global SCIP settings */
14274 SCIP_STAT* stat, /**< problem statistics */
14275 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
14276 SCIP_PROB* prob, /**< problem data */
14277 SCIP_LP* lp, /**< current LP data */
14278 SCIP_ROW* row, /**< LP row */
14279 SCIP_Real val /**< value of coefficient */
14280 )
14281 {
14282 int i;
14283
14284 assert(var != NULL);
14285 assert(set != NULL);
14286 assert(var->scip == set->scip);
14287 assert(row != NULL);
14288 assert(!SCIPsetIsInfinity(set, REALABS(val)));
14289
14290 SCIPsetDebugMsg(set, "adding coefficient %g<%s> to row <%s>\n", val, var->name, row->name);
14291
14292 if ( SCIPsetIsZero(set, val) )
14293 return SCIP_OKAY;
14294
14295 switch( SCIPvarGetStatus(var) )
14296 {
14297 case SCIP_VARSTATUS_ORIGINAL:
14298 if( var->data.original.transvar == NULL )
14299 {
14300 SCIPerrorMessage("cannot add untransformed original variable <%s> to LP row <%s>\n", var->name, row->name);
14301 return SCIP_INVALIDDATA;
14302 }
14303 SCIP_CALL( SCIPvarAddToRow(var->data.original.transvar, blkmem, set, stat, eventqueue, prob, lp, row, val) );
14304 return SCIP_OKAY;
14305
14306 case SCIP_VARSTATUS_LOOSE:
14307 /* add globally fixed variables as constant */
14308 if( SCIPsetIsEQ(set, var->glbdom.lb, var->glbdom.ub) )
14309 {
14310 SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, val * var->glbdom.lb) );
14311 return SCIP_OKAY;
14312 }
14313 /* convert loose variable into column */
14314 SCIP_CALL( SCIPvarColumn(var, blkmem, set, stat, prob, lp) );
14315 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
14316 /*lint -fallthrough*/
14317
14318 case SCIP_VARSTATUS_COLUMN:
14319 assert(var->data.col != NULL);
14320 assert(var->data.col->var == var);
14321 SCIP_CALL( SCIProwIncCoef(row, blkmem, set, eventqueue, lp, var->data.col, val) );
14322 return SCIP_OKAY;
14323
14324 case SCIP_VARSTATUS_FIXED:
14325 assert(var->glbdom.lb == var->glbdom.ub); /*lint !e777*/
14326 assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
14327 assert(var->locdom.lb == var->glbdom.lb); /*lint !e777*/
14328 assert(!SCIPsetIsInfinity(set, REALABS(var->locdom.lb)));
14329 SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, val * var->locdom.lb) );
14330 return SCIP_OKAY;
14331
14332 case SCIP_VARSTATUS_AGGREGATED:
14333 assert(!var->donotaggr);
14334 assert(var->data.aggregate.var != NULL);
14335 SCIP_CALL( SCIPvarAddToRow(var->data.aggregate.var, blkmem, set, stat, eventqueue, prob, lp,
14336 row, var->data.aggregate.scalar * val) );
14337 SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, var->data.aggregate.constant * val) );
14338 return SCIP_OKAY;
14339
14340 case SCIP_VARSTATUS_MULTAGGR:
14341 assert(!var->donotmultaggr);
14342 assert(var->data.multaggr.vars != NULL);
14343 assert(var->data.multaggr.scalars != NULL);
14344 /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
14345 * assert(var->data.multaggr.nvars >= 2);
14346 */
14347 for( i = 0; i < var->data.multaggr.nvars; ++i )
14348 {
14349 SCIP_CALL( SCIPvarAddToRow(var->data.multaggr.vars[i], blkmem, set, stat, eventqueue, prob, lp,
14350 row, var->data.multaggr.scalars[i] * val) );
14351 }
14352 SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, var->data.multaggr.constant * val) );
14353 return SCIP_OKAY;
14354
14355 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
14356 assert(var->negatedvar != NULL);
14357 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
14358 assert(var->negatedvar->negatedvar == var);
14359 SCIP_CALL( SCIPvarAddToRow(var->negatedvar, blkmem, set, stat, eventqueue, prob, lp, row, -val) );
14360 SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, var->data.negate.constant * val) );
14361 return SCIP_OKAY;
14362
14363 default:
14364 SCIPerrorMessage("unknown variable status\n");
14365 return SCIP_INVALIDDATA;
14366 }
14367 }
14368
14369 /* optionally, define this compiler flag to write complete variable histories to a file */
14370 #ifdef SCIP_HISTORYTOFILE
14371 SCIP_Longint counter = 0l;
14372 const char* historypath="."; /* allows for user-defined path; use '.' for calling directory of SCIP */
14373 #include "scip/scip.h"
14374 #endif
14375
14376 /** updates the pseudo costs of the given variable and the global pseudo costs after a change of
14377 * "solvaldelta" in the variable's solution value and resulting change of "objdelta" in the in the LP's objective value
14378 */
14379 SCIP_RETCODE SCIPvarUpdatePseudocost(
14380 SCIP_VAR* var, /**< problem variable */
14381 SCIP_SET* set, /**< global SCIP settings */
14382 SCIP_STAT* stat, /**< problem statistics */
14383 SCIP_Real solvaldelta, /**< difference of variable's new LP value - old LP value */
14384 SCIP_Real objdelta, /**< difference of new LP's objective value - old LP's objective value */
14385 SCIP_Real weight /**< weight in (0,1] of this update in pseudo cost sum */
14386 )
14387 {
14388 SCIP_Real oldrootpseudocosts;
14389 assert(var != NULL);
14390 assert(set != NULL);
14391 assert(var->scip == set->scip);
14392 assert(stat != NULL);
14393
14394 /* check if history statistics should be collected for a variable */
14395 if( !stat->collectvarhistory )
14396 return SCIP_OKAY;
14397
14398 switch( SCIPvarGetStatus(var) )
14399 {
14400 case SCIP_VARSTATUS_ORIGINAL:
14401 if( var->data.original.transvar == NULL )
14402 {
14403 SCIPerrorMessage("cannot update pseudo costs of original untransformed variable\n");
14404 return SCIP_INVALIDDATA;
14405 }
14406 SCIP_CALL( SCIPvarUpdatePseudocost(var->data.original.transvar, set, stat, solvaldelta, objdelta, weight) );
14407 return SCIP_OKAY;
14408
14409 case SCIP_VARSTATUS_LOOSE:
14410 case SCIP_VARSTATUS_COLUMN:
14411 /* store old pseudo-costs for root LP best-estimate update */
14412 oldrootpseudocosts = SCIPvarGetMinPseudocostScore(var, stat, set, SCIPvarGetRootSol(var));
14413
14414 /* update history */
14415 SCIPhistoryUpdatePseudocost(var->history, set, solvaldelta, objdelta, weight);
14416 SCIPhistoryUpdatePseudocost(var->historycrun, set, solvaldelta, objdelta, weight);
14417 SCIPhistoryUpdatePseudocost(stat->glbhistory, set, solvaldelta, objdelta, weight);
14418 SCIPhistoryUpdatePseudocost(stat->glbhistorycrun, set, solvaldelta, objdelta, weight);
14419
14420 /* update root LP best-estimate */
14421 SCIP_CALL( SCIPstatUpdateVarRootLPBestEstimate(stat, set, var, oldrootpseudocosts) );
14422
14423 /* append history to file */
14424 #ifdef SCIP_HISTORYTOFILE
14425 {
14426 FILE* f;
14427 char filename[256];
14428 SCIP_NODE* currentnode;
14429 SCIP_NODE* parentnode;
14430 currentnode = SCIPgetFocusNode(set->scip);
14431 parentnode = SCIPnodeGetParent(currentnode);
14432
14433 sprintf(filename, "%s/%s.pse", historypath, SCIPgetProbName(set->scip));
14434 f = fopen(filename, "a");
14435 if( NULL != f )
14436 {
14437 fprintf(f, "%lld %s \t %lld \t %lld \t %lld \t %d \t %15.9f \t %.3f\n",
14438 ++counter,
14439 SCIPvarGetName(var),
14440 SCIPnodeGetNumber(currentnode),
14441 parentnode != NULL ? SCIPnodeGetNumber(parentnode) : -1,
14442 SCIPgetNLPIterations(set->scip),
14443 SCIPgetDepth(set->scip),
14444 objdelta,
14445 solvaldelta);
14446 fclose(f);
14447 }
14448 }
14449 #endif
14450 return SCIP_OKAY;
14451
14452 case SCIP_VARSTATUS_FIXED:
14453 SCIPerrorMessage("cannot update pseudo cost values of a fixed variable\n");
14454 return SCIP_INVALIDDATA;
14455
14456 case SCIP_VARSTATUS_AGGREGATED:
14457 assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
14458 SCIP_CALL( SCIPvarUpdatePseudocost(var->data.aggregate.var, set, stat,
14459 solvaldelta/var->data.aggregate.scalar, objdelta, weight) );
14460 return SCIP_OKAY;
14461
14462 case SCIP_VARSTATUS_MULTAGGR:
14463 SCIPerrorMessage("cannot update pseudo cost values of a multi-aggregated variable\n");
14464 return SCIP_INVALIDDATA;
14465
14466 case SCIP_VARSTATUS_NEGATED:
14467 SCIP_CALL( SCIPvarUpdatePseudocost(var->negatedvar, set, stat, -solvaldelta, objdelta, weight) );
14468 return SCIP_OKAY;
14469
14470 default:
14471 SCIPerrorMessage("unknown variable status\n");
14472 return SCIP_INVALIDDATA;
14473 }
14474 }
14475
14476 /** gets the variable's pseudo cost value for the given step size "solvaldelta" in the variable's LP solution value */
14477 SCIP_Real SCIPvarGetPseudocost(
14478 SCIP_VAR* var, /**< problem variable */
14479 SCIP_STAT* stat, /**< problem statistics */
14480 SCIP_Real solvaldelta /**< difference of variable's new LP value - old LP value */
14481 )
14482 {
14483 SCIP_BRANCHDIR dir;
14484
14485 assert(var != NULL);
14486 assert(stat != NULL);
14487
14488 switch( SCIPvarGetStatus(var) )
14489 {
14490 case SCIP_VARSTATUS_ORIGINAL:
14491 if( var->data.original.transvar == NULL )
14492 return SCIPhistoryGetPseudocost(stat->glbhistory, solvaldelta);
14493 else
14494 return SCIPvarGetPseudocost(var->data.original.transvar, stat, solvaldelta);
14495
14496 case SCIP_VARSTATUS_LOOSE:
14497 case SCIP_VARSTATUS_COLUMN:
14498 dir = (solvaldelta >= 0.0 ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);
14499
14500 return SCIPhistoryGetPseudocostCount(var->history, dir) > 0.0
14501 ? SCIPhistoryGetPseudocost(var->history, solvaldelta)
14502 : SCIPhistoryGetPseudocost(stat->glbhistory, solvaldelta);
14503
14504 case SCIP_VARSTATUS_FIXED:
14505 return 0.0;
14506
14507 case SCIP_VARSTATUS_AGGREGATED:
14508 return SCIPvarGetPseudocost(var->data.aggregate.var, stat, var->data.aggregate.scalar * solvaldelta);
14509
14510 case SCIP_VARSTATUS_MULTAGGR:
14511 return 0.0;
14512
14513 case SCIP_VARSTATUS_NEGATED:
14514 return SCIPvarGetPseudocost(var->negatedvar, stat, -solvaldelta);
14515
14516 default:
14517 SCIPerrorMessage("unknown variable status\n");
14518 SCIPABORT();
14519 return 0.0; /*lint !e527*/
14520 }
14521 }
14522
14523 /** gets the variable's pseudo cost value for the given step size "solvaldelta" in the variable's LP solution value,
14524 * only using the pseudo cost information of the current run
14525 */
14526 SCIP_Real SCIPvarGetPseudocostCurrentRun(
14527 SCIP_VAR* var, /**< problem variable */
14528 SCIP_STAT* stat, /**< problem statistics */
14529 SCIP_Real solvaldelta /**< difference of variable's new LP value - old LP value */
14530 )
14531 {
14532 SCIP_BRANCHDIR dir;
14533
14534 assert(var != NULL);
14535 assert(stat != NULL);
14536
14537 switch( SCIPvarGetStatus(var) )
14538 {
14539 case SCIP_VARSTATUS_ORIGINAL:
14540 if( var->data.original.transvar == NULL )
14541 return SCIPhistoryGetPseudocost(stat->glbhistorycrun, solvaldelta);
14542 else
14543 return SCIPvarGetPseudocostCurrentRun(var->data.original.transvar, stat, solvaldelta);
14544
14545 case SCIP_VARSTATUS_LOOSE:
14546 case SCIP_VARSTATUS_COLUMN:
14547 dir = (solvaldelta >= 0.0 ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);
14548
14549 return SCIPhistoryGetPseudocostCount(var->historycrun, dir) > 0.0
14550 ? SCIPhistoryGetPseudocost(var->historycrun, solvaldelta)
14551 : SCIPhistoryGetPseudocost(stat->glbhistorycrun, solvaldelta);
14552
14553 case SCIP_VARSTATUS_FIXED:
14554 return 0.0;
14555
14556 case SCIP_VARSTATUS_AGGREGATED:
14557 return SCIPvarGetPseudocostCurrentRun(var->data.aggregate.var, stat, var->data.aggregate.scalar * solvaldelta);
14558
14559 case SCIP_VARSTATUS_MULTAGGR:
14560 return 0.0;
14561
14562 case SCIP_VARSTATUS_NEGATED:
14563 return SCIPvarGetPseudocostCurrentRun(var->negatedvar, stat, -solvaldelta);
14564
14565 default:
14566 SCIPerrorMessage("unknown variable status\n");
14567 SCIPABORT();
14568 return 0.0; /*lint !e527*/
14569 }
14570 }
14571
14572 /** gets the variable's (possible fractional) number of pseudo cost updates for the given direction */
14573 SCIP_Real SCIPvarGetPseudocostCount(
14574 SCIP_VAR* var, /**< problem variable */
14575 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
14576 )
14577 {
14578 assert(var != NULL);
14579 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
14580
14581 switch( SCIPvarGetStatus(var) )
14582 {
14583 case SCIP_VARSTATUS_ORIGINAL:
14584 if( var->data.original.transvar == NULL )
14585 return 0.0;
14586 else
14587 return SCIPvarGetPseudocostCount(var->data.original.transvar, dir);
14588
14589 case SCIP_VARSTATUS_LOOSE:
14590 case SCIP_VARSTATUS_COLUMN:
14591 return SCIPhistoryGetPseudocostCount(var->history, dir);
14592
14593 case SCIP_VARSTATUS_FIXED:
14594 return 0.0;
14595
14596 case SCIP_VARSTATUS_AGGREGATED:
14597 if( var->data.aggregate.scalar > 0.0 )
14598 return SCIPvarGetPseudocostCount(var->data.aggregate.var, dir);
14599 else
14600 return SCIPvarGetPseudocostCount(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
14601
14602 case SCIP_VARSTATUS_MULTAGGR:
14603 return 0.0;
14604
14605 case SCIP_VARSTATUS_NEGATED:
14606 return SCIPvarGetPseudocostCount(var->negatedvar, SCIPbranchdirOpposite(dir));
14607
14608 default:
14609 SCIPerrorMessage("unknown variable status\n");
14610 SCIPABORT();
14611 return 0.0; /*lint !e527*/
14612 }
14613 }
14614
14615 /** gets the variable's (possible fractional) number of pseudo cost updates for the given direction,
14616 * only using the pseudo cost information of the current run
14617 */
14618 SCIP_Real SCIPvarGetPseudocostCountCurrentRun(
14619 SCIP_VAR* var, /**< problem variable */
14620 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
14621 )
14622 {
14623 assert(var != NULL);
14624 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
14625
14626 switch( SCIPvarGetStatus(var) )
14627 {
14628 case SCIP_VARSTATUS_ORIGINAL:
14629 if( var->data.original.transvar == NULL )
14630 return 0.0;
14631 else
14632 return SCIPvarGetPseudocostCountCurrentRun(var->data.original.transvar, dir);
14633
14634 case SCIP_VARSTATUS_LOOSE:
14635 case SCIP_VARSTATUS_COLUMN:
14636 return SCIPhistoryGetPseudocostCount(var->historycrun, dir);
14637
14638 case SCIP_VARSTATUS_FIXED:
14639 return 0.0;
14640
14641 case SCIP_VARSTATUS_AGGREGATED:
14642 if( var->data.aggregate.scalar > 0.0 )
14643 return SCIPvarGetPseudocostCountCurrentRun(var->data.aggregate.var, dir);
14644 else
14645 return SCIPvarGetPseudocostCountCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
14646
14647 case SCIP_VARSTATUS_MULTAGGR:
14648 return 0.0;
14649
14650 case SCIP_VARSTATUS_NEGATED:
14651 return SCIPvarGetPseudocostCountCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));
14652
14653 default:
14654 SCIPerrorMessage("unknown variable status\n");
14655 SCIPABORT();
14656 return 0.0; /*lint !e527*/
14657 }
14658 }
14659
14660 /** compares both possible directions for rounding the given solution value and returns the minimum pseudo-costs of the variable */
14661 SCIP_Real SCIPvarGetMinPseudocostScore(
14662 SCIP_VAR* var, /**< problem variable */
14663 SCIP_STAT* stat, /**< problem statistics */
14664 SCIP_SET* set, /**< global SCIP settings */
14665 SCIP_Real solval /**< solution value, e.g., LP solution value */
14666 )
14667 {
14668 SCIP_Real upscore;
14669 SCIP_Real downscore;
14670 SCIP_Real solvaldeltaup;
14671 SCIP_Real solvaldeltadown;
14672
14673 /* LP root estimate only works for variables with fractional LP root solution */
14674 if( SCIPsetIsFeasIntegral(set, solval) )
14675 return 0.0;
14676
14677 /* no min pseudo-cost score is calculated as long as the variable was not initialized in a direction */
14678 if( SCIPvarGetPseudocostCount(var, SCIP_BRANCHDIR_DOWNWARDS) < 1.0 || SCIPvarGetPseudocostCount(var, SCIP_BRANCHDIR_UPWARDS) < 1.0 )
14679 return 0.0;
14680
14681 /* compute delta's to ceil and floor of root LP solution value */
14682 solvaldeltaup = SCIPsetCeil(set, solval) - solval;
14683 solvaldeltadown = SCIPsetFloor(set, solval) - solval;
14684
14685 upscore = SCIPvarGetPseudocost(var, stat, solvaldeltaup);
14686 downscore = SCIPvarGetPseudocost(var, stat, solvaldeltadown);
14687
14688 return MIN(upscore, downscore);
14689 }
14690
14691 /** gets the an estimate of the variable's pseudo cost variance in direction \p dir */
14692 SCIP_Real SCIPvarGetPseudocostVariance(
14693 SCIP_VAR* var, /**< problem variable */
14694 SCIP_BRANCHDIR dir, /**< branching direction (downwards, or upwards) */
14695 SCIP_Bool onlycurrentrun /**< return pseudo cost variance only for current branch and bound run */
14696 )
14697 {
14698 assert(var != NULL);
14699 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
14700
14701 switch( SCIPvarGetStatus(var) )
14702 {
14703 case SCIP_VARSTATUS_ORIGINAL:
14704 if( var->data.original.transvar == NULL )
14705 return 0.0;
14706 else
14707 return SCIPvarGetPseudocostVariance(var->data.original.transvar, dir, onlycurrentrun);
14708
14709 case SCIP_VARSTATUS_LOOSE:
14710 case SCIP_VARSTATUS_COLUMN:
14711 if( onlycurrentrun )
14712 return SCIPhistoryGetPseudocostVariance(var->historycrun, dir);
14713 else
14714 return SCIPhistoryGetPseudocostVariance(var->history, dir);
14715
14716 case SCIP_VARSTATUS_FIXED:
14717 return 0.0;
14718
14719 case SCIP_VARSTATUS_AGGREGATED:
14720 if( var->data.aggregate.scalar > 0.0 )
14721 return SCIPvarGetPseudocostVariance(var->data.aggregate.var, dir, onlycurrentrun);
14722 else
14723 return SCIPvarGetPseudocostVariance(var->data.aggregate.var, SCIPbranchdirOpposite(dir), onlycurrentrun);
14724
14725 case SCIP_VARSTATUS_MULTAGGR:
14726 return 0.0;
14727
14728 case SCIP_VARSTATUS_NEGATED:
14729 return SCIPvarGetPseudocostVariance(var->negatedvar, SCIPbranchdirOpposite(dir), onlycurrentrun);
14730
14731 default:
14732 SCIPerrorMessage("unknown variable status\n");
14733 SCIPABORT();
14734 return 0.0; /*lint !e527*/
14735 }
14736 }
14737
14738 /** calculates a confidence bound for this variable under the assumption of normally distributed pseudo costs
14739 *
14740 * The confidence bound \f$ \theta \geq 0\f$ denotes the interval borders \f$ [X - \theta, \ X + \theta]\f$, which contains
14741 * the true pseudo costs of the variable, i.e., the expected value of the normal distribution, with a probability
14742 * of 2 * clevel - 1.
14743 *
14744 * @return value of confidence bound for this variable
14745 */
14746 SCIP_Real SCIPvarCalcPscostConfidenceBound(
14747 SCIP_VAR* var, /**< variable in question */
14748 SCIP_SET* set, /**< global SCIP settings */
14749 SCIP_BRANCHDIR dir, /**< the branching direction for the confidence bound */
14750 SCIP_Bool onlycurrentrun, /**< should only the current run be taken into account */
14751 SCIP_CONFIDENCELEVEL clevel /**< confidence level for the interval */
14752 )
14753 {
14754 SCIP_Real confidencebound;
14755
14756 confidencebound = SCIPvarGetPseudocostVariance(var, dir, onlycurrentrun);
14757 if( SCIPsetIsFeasPositive(set, confidencebound) )
14758 {
14759 SCIP_Real count;
14760
14761 if( onlycurrentrun )
14762 count = SCIPvarGetPseudocostCountCurrentRun(var, dir);
14763 else
14764 count = SCIPvarGetPseudocostCount(var, dir);
14765 /* assertion is valid because variance is positive */
14766 assert(count >= 1.9);
14767
14768 confidencebound /= count; /*lint !e414 division by zero can obviously not occur */
14769 confidencebound = sqrt(confidencebound);
14770
14771 /* the actual, underlying distribution of the mean is a student-t-distribution with degrees of freedom equal to
14772 * the number of pseudo cost evaluations of this variable in the respective direction. */
14773 confidencebound *= SCIPstudentTGetCriticalValue(clevel, (int)SCIPsetFloor(set, count) - 1);
14774 }
14775 else
14776 confidencebound = 0.0;
14777
14778 return confidencebound;
14779 }
14780
14781 /** check if the current pseudo cost relative error in a direction violates the given threshold. The Relative
14782 * Error is calculated at a specific confidence level
14783 */
14784 SCIP_Bool SCIPvarIsPscostRelerrorReliable(
14785 SCIP_VAR* var, /**< variable in question */
14786 SCIP_SET* set, /**< global SCIP settings */
14787 SCIP_STAT* stat, /**< problem statistics */
14788 SCIP_Real threshold, /**< threshold for relative errors to be considered reliable (enough) */
14789 SCIP_CONFIDENCELEVEL clevel /**< a given confidence level */
14790 )
14791 {
14792 SCIP_Real downsize;
14793 SCIP_Real upsize;
14794 SCIP_Real size;
14795 SCIP_Real relerrorup;
14796 SCIP_Real relerrordown;
14797 SCIP_Real relerror;
14798
14799 /* check, if the pseudo cost score of the variable is reliable */
14800 downsize = SCIPvarGetPseudocostCountCurrentRun(var, SCIP_BRANCHDIR_DOWNWARDS);
14801 upsize = SCIPvarGetPseudocostCountCurrentRun(var, SCIP_BRANCHDIR_UPWARDS);
14802 size = MIN(downsize, upsize);
14803
14804 /* Pseudo costs relative error can only be reliable if both directions have been tried at least twice */
14805 if( size <= 1.9 )
14806 return FALSE;
14807
14808 /* use the relative error between the current mean pseudo cost value of the candidate and its upper
14809 * confidence interval bound at confidence level of 95% for individual variable reliability.
14810 * this is only possible if we have at least 2 measurements and therefore a valid variance estimate.
14811 */
14812 if( downsize >= 1.9 )
14813 {
14814 SCIP_Real normval;
14815
14816 relerrordown = SCIPvarCalcPscostConfidenceBound(var, set, SCIP_BRANCHDIR_DOWNWARDS, TRUE, clevel);
14817 normval = SCIPvarGetPseudocostCurrentRun(var, stat, -1.0);
14818 normval = MAX(1.0, normval);
14819
14820 relerrordown /= normval;
14821 }
14822 else
14823 relerrordown = 0.0;
14824
14825 if( upsize >= 1.9 )
14826 {
14827 SCIP_Real normval;
14828
14829 relerrorup = SCIPvarCalcPscostConfidenceBound(var, set, SCIP_BRANCHDIR_UPWARDS, TRUE, clevel);
14830 normval = SCIPvarGetPseudocostCurrentRun(var, stat, +1.0);
14831 normval = MAX(1.0, normval);
14832 relerrorup /= normval;
14833 }
14834 else
14835 relerrorup = 0.0;
14836
14837 /* consider the relative error threshold violated, if it is violated in at least one branching direction */
14838 relerror = MAX(relerrorup, relerrordown);
14839
14840 return (relerror <= threshold);
14841 }
14842
14843 /** check if variable pseudo-costs have a significant difference in location. The significance depends on
14844 * the choice of \p clevel and on the kind of tested hypothesis. The one-sided hypothesis, which
14845 * should be rejected, is that fracy * mu_y >= fracx * mu_x, where mu_y and mu_x denote the
14846 * unknown location means of the underlying pseudo-cost distributions of x and y.
14847 *
14848 * This method is applied best if variable x has a better pseudo-cost score than y. The method hypothesizes that y were actually
14849 * better than x (despite the current information), meaning that y can be expected to yield branching
14850 * decisions as least as good as x in the long run. If the method returns TRUE, the current history information is
14851 * sufficient to safely rely on the alternative hypothesis that x yields indeed a better branching score (on average)
14852 * than y.
14853 *
14854 * @note The order of x and y matters for the one-sided hypothesis
14855 *
14856 * @note set \p onesided to FALSE if you are not sure which variable is better. The hypothesis tested then reads
14857 * fracy * mu_y == fracx * mu_x vs the alternative hypothesis fracy * mu_y != fracx * mu_x.
14858 *
14859 * @return TRUE if the hypothesis can be safely rejected at the given confidence level
14860 */
14861 SCIP_Bool SCIPvarSignificantPscostDifference(
14862 SCIP_SET* set, /**< global SCIP settings */
14863 SCIP_STAT* stat, /**< problem statistics */
14864 SCIP_VAR* varx, /**< variable x */
14865 SCIP_Real fracx, /**< the fractionality of variable x */
14866 SCIP_VAR* vary, /**< variable y */
14867 SCIP_Real fracy, /**< the fractionality of variable y */
14868 SCIP_BRANCHDIR dir, /**< branching direction */
14869 SCIP_CONFIDENCELEVEL clevel, /**< confidence level for rejecting hypothesis */
14870 SCIP_Bool onesided /**< should a one-sided hypothesis y >= x be tested? */
14871 )
14872 {
14873 SCIP_Real meanx;
14874 SCIP_Real meany;
14875 SCIP_Real variancex;
14876 SCIP_Real variancey;
14877 SCIP_Real countx;
14878 SCIP_Real county;
14879 SCIP_Real tresult;
14880 SCIP_Real realdirection;
14881
14882 if( varx == vary )
14883 return FALSE;
14884
14885 countx = SCIPvarGetPseudocostCount(varx, dir);
14886 county = SCIPvarGetPseudocostCount(vary, dir);
14887
14888 /* if not at least 2 measurements were taken, return FALSE */
14889 if( countx <= 1.9 || county <= 1.9 )
14890 return FALSE;
14891
14892 realdirection = (dir == SCIP_BRANCHDIR_DOWNWARDS ? -1.0 : 1.0);
14893
14894 meanx = fracx * SCIPvarGetPseudocost(varx, stat, realdirection);
14895 meany = fracy * SCIPvarGetPseudocost(vary, stat, realdirection);
14896
14897 variancex = SQR(fracx) * SCIPvarGetPseudocostVariance(varx, dir, FALSE);
14898 variancey = SQR(fracy) * SCIPvarGetPseudocostVariance(vary, dir, FALSE);
14899
14900 /* if there is no variance, the means are taken from a constant distribution */
14901 if( SCIPsetIsFeasEQ(set, variancex + variancey, 0.0) )
14902 return (onesided ? SCIPsetIsFeasGT(set, meanx, meany) : !SCIPsetIsFeasEQ(set, meanx, meany));
14903
14904 tresult = SCIPcomputeTwoSampleTTestValue(meanx, meany, variancex, variancey, countx, county);
14905
14906 /* for the two-sided hypothesis, just take the absolute of t */
14907 if( !onesided )
14908 tresult = REALABS(tresult);
14909
14910 return (tresult >= SCIPstudentTGetCriticalValue(clevel, (int)(countx + county - 2)));
14911 }
14912
14913 /** tests at a given confidence level whether the variable pseudo-costs only have a small probability to
14914 * exceed a \p threshold. This is useful to determine if past observations provide enough evidence
14915 * to skip an expensive strong-branching step if there is already a candidate that has been proven to yield an improvement
14916 * of at least \p threshold.
14917 *
14918 * @note use \p clevel to adjust the level of confidence. For SCIP_CONFIDENCELEVEL_MIN, the method returns TRUE if
14919 * the estimated probability to exceed \p threshold is less than 25 %.
14920 *
14921 * @see SCIP_Confidencelevel for a list of available levels. The used probability limits refer to the one-sided levels
14922 * of confidence.
14923 *
14924 * @return TRUE if the variable pseudo-cost probabilistic model is likely to be smaller than \p threshold
14925 * at the given confidence level \p clevel.
14926 */
14927 SCIP_Bool SCIPvarPscostThresholdProbabilityTest(
14928 SCIP_SET* set, /**< global SCIP settings */
14929 SCIP_STAT* stat, /**< problem statistics */
14930 SCIP_VAR* var, /**< variable x */
14931 SCIP_Real frac, /**< the fractionality of variable x */
14932 SCIP_Real threshold, /**< the threshold to test against */
14933 SCIP_BRANCHDIR dir, /**< branching direction */
14934 SCIP_CONFIDENCELEVEL clevel /**< confidence level for rejecting hypothesis */
14935 )
14936 {
14937 SCIP_Real mean;
14938 SCIP_Real variance;
14939 SCIP_Real count;
14940 SCIP_Real realdirection;
14941 SCIP_Real probability;
14942 SCIP_Real problimit;
14943
14944 count = SCIPvarGetPseudocostCount(var, dir);
14945
14946 /* if not at least 2 measurements were taken, return FALSE */
14947 if( count <= 1.9 )
14948 return FALSE;
14949
14950 realdirection = (dir == SCIP_BRANCHDIR_DOWNWARDS ? -1.0 : 1.0);
14951
14952 mean = frac * SCIPvarGetPseudocost(var, stat, realdirection);
14953 variance = SQR(frac) * SCIPvarGetPseudocostVariance(var, dir, FALSE);
14954
14955 /* if mean is at least threshold, it has at least a 50% probability to exceed threshold, we therefore return FALSE */
14956 if( SCIPsetIsFeasGE(set, mean, threshold) )
14957 return FALSE;
14958
14959 /* if there is no variance, the means are taken from a constant distribution */
14960 if( SCIPsetIsFeasEQ(set, variance, 0.0) )
14961 return SCIPsetIsFeasLT(set, mean, threshold);
14962
14963 /* obtain probability of a normally distributed random variable at given mean and variance to yield at most threshold */
14964 probability = SCIPnormalCDF(mean, variance, threshold);
14965
14966 /* determine a probability limit corresponding to the given confidence level */
14967 switch( clevel )
14968 {
14969 case SCIP_CONFIDENCELEVEL_MIN:
14970 problimit = 0.75;
14971 break;
14972 case SCIP_CONFIDENCELEVEL_LOW:
14973 problimit = 0.875;
14974 break;
14975 case SCIP_CONFIDENCELEVEL_MEDIUM:
14976 problimit = 0.9;
14977 break;
14978 case SCIP_CONFIDENCELEVEL_HIGH:
14979 problimit = 0.95;
14980 break;
14981 case SCIP_CONFIDENCELEVEL_MAX:
14982 problimit = 0.975;
14983 break;
14984 default:
14985 problimit = -1;
14986 SCIPerrorMessage("Confidence level set to unknown value <%d>", (int)clevel);
14987 SCIPABORT();
14988 break;
14989 }
14990
14991 return (probability >= problimit);
14992 }
14993
14994 /** find the corresponding history entry if already existing, otherwise create new entry */
14995 static
14996 SCIP_RETCODE findValuehistoryEntry(
14997 SCIP_VAR* var, /**< problem variable */
14998 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
14999 BMS_BLKMEM* blkmem, /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
15000 SCIP_SET* set, /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
15001 SCIP_HISTORY** history /**< pointer to store the value based history, or NULL */
15002 )
15003 {
15004 assert(var != NULL);
15005 assert(blkmem != NULL);
15006 assert(set != NULL);
15007 assert(history != NULL);
15008
15009 (*history) = NULL;
15010
15011 if( var->valuehistory == NULL )
15012 {
15013 SCIP_CALL( SCIPvaluehistoryCreate(&var->valuehistory, blkmem) );
15014 }
15015
15016 SCIP_CALL( SCIPvaluehistoryFind(var->valuehistory, blkmem, set, value, history) );
15017
15018 return SCIP_OKAY;
15019 }
15020
15021 /** check if value based history should be used */
15022 static
15023 SCIP_Bool useValuehistory(
15024 SCIP_VAR* var, /**< problem variable */
15025 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
15026 SCIP_SET* set /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
15027 )
15028 {
15029 /* check if the domain value is unknown (not specific) */
15030 if( value == SCIP_UNKNOWN ) /*lint !e777*/
15031 return FALSE;
15032
15033 assert(set != NULL);
15034
15035 /* check if value based history should be collected */
15036 if( !set->history_valuebased )
15037 return FALSE;
15038
15039 /* value based history is not collected for binary variable since the standard history already contains all information */
15040 if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY )
15041 return FALSE;
15042
15043 /* value based history is not collected for continuous variables */
15044 if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS )
15045 return FALSE;
15046
15047 return TRUE;
15048 }
15049
15050 /** increases VSIDS of the variable by the given weight */
15051 SCIP_RETCODE SCIPvarIncVSIDS(
15052 SCIP_VAR* var, /**< problem variable */
15053 BMS_BLKMEM* blkmem, /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
15054 SCIP_SET* set, /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
15055 SCIP_STAT* stat, /**< problem statistics */
15056 SCIP_BRANCHDIR dir, /**< branching direction */
15057 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
15058 SCIP_Real weight /**< weight of this update in VSIDS */
15059 )
15060 {
15061 assert(var != NULL);
15062 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15063
15064 /* check if history statistics should be collected for a variable */
15065 if( !stat->collectvarhistory )
15066 return SCIP_OKAY;
15067
15068 if( SCIPsetIsZero(set, weight) )
15069 return SCIP_OKAY;
15070
15071 switch( SCIPvarGetStatus(var) )
15072 {
15073 case SCIP_VARSTATUS_ORIGINAL:
15074 if( var->data.original.transvar == NULL )
15075 {
15076 SCIPerrorMessage("cannot update VSIDS of original untransformed variable\n");
15077 return SCIP_INVALIDDATA;
15078 }
15079 SCIP_CALL( SCIPvarIncVSIDS(var->data.original.transvar, blkmem, set, stat, dir, value, weight) );
15080 return SCIP_OKAY;
15081
15082 case SCIP_VARSTATUS_LOOSE:
15083 case SCIP_VARSTATUS_COLUMN:
15084 {
15085 SCIPhistoryIncVSIDS(var->history, dir, weight);
15086 SCIPhistoryIncVSIDS(var->historycrun, dir, weight);
15087
15088 if( useValuehistory(var, value, set) )
15089 {
15090 SCIP_HISTORY* history;
15091
15092 SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
15093 assert(history != NULL);
15094
15095 SCIPhistoryIncVSIDS(history, dir, weight);
15096 SCIPsetDebugMsg(set, "variable (<%s> %s %g) + <%g> = <%g>\n", SCIPvarGetName(var), dir == SCIP_BRANCHDIR_UPWARDS ? ">=" : "<=",
15097 value, weight, SCIPhistoryGetVSIDS(history, dir));
15098 }
15099
15100 return SCIP_OKAY;
15101 }
15102 case SCIP_VARSTATUS_FIXED:
15103 SCIPerrorMessage("cannot update VSIDS of a fixed variable\n");
15104 return SCIP_INVALIDDATA;
15105
15106 case SCIP_VARSTATUS_AGGREGATED:
15107 value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;
15108
15109 if( var->data.aggregate.scalar > 0.0 )
15110 {
15111 SCIP_CALL( SCIPvarIncVSIDS(var->data.aggregate.var, blkmem, set, stat, dir, value, weight) );
15112 }
15113 else
15114 {
15115 assert(var->data.aggregate.scalar < 0.0);
15116 SCIP_CALL( SCIPvarIncVSIDS(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
15117 }
15118 return SCIP_OKAY;
15119
15120 case SCIP_VARSTATUS_MULTAGGR:
15121 SCIPerrorMessage("cannot update VSIDS of a multi-aggregated variable\n");
15122 return SCIP_INVALIDDATA;
15123
15124 case SCIP_VARSTATUS_NEGATED:
15125 value = 1.0 - value;
15126
15127 SCIP_CALL( SCIPvarIncVSIDS(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
15128 return SCIP_OKAY;
15129
15130 default:
15131 SCIPerrorMessage("unknown variable status\n");
15132 return SCIP_INVALIDDATA;
15133 }
15134 }
15135
15136 /** scales the VSIDS of the variable by the given scalar */
15137 SCIP_RETCODE SCIPvarScaleVSIDS(
15138 SCIP_VAR* var, /**< problem variable */
15139 SCIP_Real scalar /**< scalar to multiply the VSIDSs with */
15140 )
15141 {
15142 assert(var != NULL);
15143
15144 switch( SCIPvarGetStatus(var) )
15145 {
15146 case SCIP_VARSTATUS_ORIGINAL:
15147 if( var->data.original.transvar == NULL )
15148 {
15149 SCIPerrorMessage("cannot update VSIDS of original untransformed variable\n");
15150 return SCIP_INVALIDDATA;
15151 }
15152 SCIP_CALL( SCIPvarScaleVSIDS(var->data.original.transvar, scalar) );
15153 return SCIP_OKAY;
15154
15155 case SCIP_VARSTATUS_LOOSE:
15156 case SCIP_VARSTATUS_COLUMN:
15157 {
15158 SCIPhistoryScaleVSIDS(var->history, scalar);
15159 SCIPhistoryScaleVSIDS(var->historycrun, scalar);
15160 SCIPvaluehistoryScaleVSIDS(var->valuehistory, scalar);
15161
15162 return SCIP_OKAY;
15163 }
15164 case SCIP_VARSTATUS_FIXED:
15165 SCIPerrorMessage("cannot update VSIDS of a fixed variable\n");
15166 return SCIP_INVALIDDATA;
15167
15168 case SCIP_VARSTATUS_AGGREGATED:
15169 SCIP_CALL( SCIPvarScaleVSIDS(var->data.aggregate.var, scalar) );
15170 return SCIP_OKAY;
15171
15172 case SCIP_VARSTATUS_MULTAGGR:
15173 SCIPerrorMessage("cannot update VSIDS of a multi-aggregated variable\n");
15174 return SCIP_INVALIDDATA;
15175
15176 case SCIP_VARSTATUS_NEGATED:
15177 SCIP_CALL( SCIPvarScaleVSIDS(var->negatedvar, scalar) );
15178 return SCIP_OKAY;
15179
15180 default:
15181 SCIPerrorMessage("unknown variable status\n");
15182 return SCIP_INVALIDDATA;
15183 }
15184 }
15185
15186 /** increases the number of active conflicts by one and the overall length of the variable by the given length */
15187 SCIP_RETCODE SCIPvarIncNActiveConflicts(
15188 SCIP_VAR* var, /**< problem variable */
15189 BMS_BLKMEM* blkmem, /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
15190 SCIP_SET* set, /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
15191 SCIP_STAT* stat, /**< problem statistics */
15192 SCIP_BRANCHDIR dir, /**< branching direction */
15193 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
15194 SCIP_Real length /**< length of the conflict */
15195 )
15196 {
15197 assert(var != NULL);
15198 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15199
15200 /* check if history statistics should be collected for a variable */
15201 if( !stat->collectvarhistory )
15202 return SCIP_OKAY;
15203
15204 switch( SCIPvarGetStatus(var) )
15205 {
15206 case SCIP_VARSTATUS_ORIGINAL:
15207 if( var->data.original.transvar == NULL )
15208 {
15209 SCIPerrorMessage("cannot update conflict score of original untransformed variable\n");
15210 return SCIP_INVALIDDATA;
15211 }
15212 SCIP_CALL( SCIPvarIncNActiveConflicts(var->data.original.transvar, blkmem, set, stat, dir, value, length) );
15213 return SCIP_OKAY;
15214
15215 case SCIP_VARSTATUS_LOOSE:
15216 case SCIP_VARSTATUS_COLUMN:
15217 {
15218 SCIPhistoryIncNActiveConflicts(var->history, dir, length);
15219 SCIPhistoryIncNActiveConflicts(var->historycrun, dir, length);
15220
15221 if( useValuehistory(var, value, set) )
15222 {
15223 SCIP_HISTORY* history;
15224
15225 SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
15226 assert(history != NULL);
15227
15228 SCIPhistoryIncNActiveConflicts(history, dir, length);
15229 }
15230
15231 return SCIP_OKAY;
15232 }
15233 case SCIP_VARSTATUS_FIXED:
15234 SCIPerrorMessage("cannot update conflict score of a fixed variable\n");
15235 return SCIP_INVALIDDATA;
15236
15237 case SCIP_VARSTATUS_AGGREGATED:
15238 value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;
15239
15240 if( var->data.aggregate.scalar > 0.0 )
15241 {
15242 SCIP_CALL( SCIPvarIncNActiveConflicts(var->data.aggregate.var, blkmem, set, stat, dir, value, length) );
15243 }
15244 else
15245 {
15246 assert(var->data.aggregate.scalar < 0.0);
15247 SCIP_CALL( SCIPvarIncNActiveConflicts(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, length) );
15248 }
15249 return SCIP_OKAY;
15250
15251 case SCIP_VARSTATUS_MULTAGGR:
15252 SCIPerrorMessage("cannot update conflict score of a multi-aggregated variable\n");
15253 return SCIP_INVALIDDATA;
15254
15255 case SCIP_VARSTATUS_NEGATED:
15256 value = 1.0 - value;
15257
15258 SCIP_CALL( SCIPvarIncNActiveConflicts(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, length) );
15259 return SCIP_OKAY;
15260
15261 default:
15262 SCIPerrorMessage("unknown variable status\n");
15263 return SCIP_INVALIDDATA;
15264 }
15265 }
15266
15267 /** gets the number of active conflicts containing this variable in given direction */
15268 SCIP_Longint SCIPvarGetNActiveConflicts(
15269 SCIP_VAR* var, /**< problem variable */
15270 SCIP_STAT* stat, /**< problem statistics */
15271 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15272 )
15273 {
15274 assert(var != NULL);
15275 assert(stat != NULL);
15276 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15277
15278 switch( SCIPvarGetStatus(var) )
15279 {
15280 case SCIP_VARSTATUS_ORIGINAL:
15281 if( var->data.original.transvar == NULL )
15282 return 0;
15283 else
15284 return SCIPvarGetNActiveConflicts(var->data.original.transvar, stat, dir);
15285
15286 case SCIP_VARSTATUS_LOOSE:
15287 case SCIP_VARSTATUS_COLUMN:
15288 return SCIPhistoryGetNActiveConflicts(var->history, dir);
15289
15290 case SCIP_VARSTATUS_FIXED:
15291 return 0;
15292
15293 case SCIP_VARSTATUS_AGGREGATED:
15294 if( var->data.aggregate.scalar > 0.0 )
15295 return SCIPvarGetNActiveConflicts(var->data.aggregate.var, stat, dir);
15296 else
15297 return SCIPvarGetNActiveConflicts(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
15298
15299 case SCIP_VARSTATUS_MULTAGGR:
15300 return 0;
15301
15302 case SCIP_VARSTATUS_NEGATED:
15303 return SCIPvarGetNActiveConflicts(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
15304
15305 default:
15306 SCIPerrorMessage("unknown variable status\n");
15307 SCIPABORT();
15308 return 0; /*lint !e527*/
15309 }
15310 }
15311
15312 /** gets the number of active conflicts containing this variable in given direction
15313 * in the current run
15314 */
15315 SCIP_Longint SCIPvarGetNActiveConflictsCurrentRun(
15316 SCIP_VAR* var, /**< problem variable */
15317 SCIP_STAT* stat, /**< problem statistics */
15318 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15319 )
15320 {
15321 assert(var != NULL);
15322 assert(stat != NULL);
15323 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15324
15325 switch( SCIPvarGetStatus(var) )
15326 {
15327 case SCIP_VARSTATUS_ORIGINAL:
15328 if( var->data.original.transvar == NULL )
15329 return 0;
15330 else
15331 return SCIPvarGetNActiveConflictsCurrentRun(var->data.original.transvar, stat, dir);
15332
15333 case SCIP_VARSTATUS_LOOSE:
15334 case SCIP_VARSTATUS_COLUMN:
15335 return SCIPhistoryGetNActiveConflicts(var->historycrun, dir);
15336
15337 case SCIP_VARSTATUS_FIXED:
15338 return 0;
15339
15340 case SCIP_VARSTATUS_AGGREGATED:
15341 if( var->data.aggregate.scalar > 0.0 )
15342 return SCIPvarGetNActiveConflictsCurrentRun(var->data.aggregate.var, stat, dir);
15343 else
15344 return SCIPvarGetNActiveConflictsCurrentRun(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
15345
15346 case SCIP_VARSTATUS_MULTAGGR:
15347 return 0;
15348
15349 case SCIP_VARSTATUS_NEGATED:
15350 return SCIPvarGetNActiveConflictsCurrentRun(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
15351
15352 default:
15353 SCIPerrorMessage("unknown variable status\n");
15354 SCIPABORT();
15355 return 0; /*lint !e527*/
15356 }
15357 }
15358
15359 /** gets the average conflict length in given direction due to branching on the variable */
15360 SCIP_Real SCIPvarGetAvgConflictlength(
15361 SCIP_VAR* var, /**< problem variable */
15362 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15363 )
15364 {
15365 assert(var != NULL);
15366 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15367
15368 switch( SCIPvarGetStatus(var) )
15369 {
15370 case SCIP_VARSTATUS_ORIGINAL:
15371 if( var->data.original.transvar == NULL )
15372 return 0.0;
15373 else
15374 return SCIPvarGetAvgConflictlength(var->data.original.transvar, dir);
15375
15376 case SCIP_VARSTATUS_LOOSE:
15377 case SCIP_VARSTATUS_COLUMN:
15378 return SCIPhistoryGetAvgConflictlength(var->history, dir);
15379 case SCIP_VARSTATUS_FIXED:
15380 return 0.0;
15381
15382 case SCIP_VARSTATUS_AGGREGATED:
15383 if( var->data.aggregate.scalar > 0.0 )
15384 return SCIPvarGetAvgConflictlength(var->data.aggregate.var, dir);
15385 else
15386 return SCIPvarGetAvgConflictlength(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15387
15388 case SCIP_VARSTATUS_MULTAGGR:
15389 return 0.0;
15390
15391 case SCIP_VARSTATUS_NEGATED:
15392 return SCIPvarGetAvgConflictlength(var->negatedvar, SCIPbranchdirOpposite(dir));
15393
15394 default:
15395 SCIPerrorMessage("unknown variable status\n");
15396 SCIPABORT();
15397 return 0.0; /*lint !e527*/
15398 }
15399 }
15400
15401 /** gets the average conflict length in given direction due to branching on the variable
15402 * in the current run
15403 */
15404 SCIP_Real SCIPvarGetAvgConflictlengthCurrentRun(
15405 SCIP_VAR* var, /**< problem variable */
15406 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15407 )
15408 {
15409 assert(var != NULL);
15410 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15411
15412 switch( SCIPvarGetStatus(var) )
15413 {
15414 case SCIP_VARSTATUS_ORIGINAL:
15415 if( var->data.original.transvar == NULL )
15416 return 0.0;
15417 else
15418 return SCIPvarGetAvgConflictlengthCurrentRun(var->data.original.transvar, dir);
15419
15420 case SCIP_VARSTATUS_LOOSE:
15421 case SCIP_VARSTATUS_COLUMN:
15422 return SCIPhistoryGetAvgConflictlength(var->historycrun, dir);
15423
15424 case SCIP_VARSTATUS_FIXED:
15425 return 0.0;
15426
15427 case SCIP_VARSTATUS_AGGREGATED:
15428 if( var->data.aggregate.scalar > 0.0 )
15429 return SCIPvarGetAvgConflictlengthCurrentRun(var->data.aggregate.var, dir);
15430 else
15431 return SCIPvarGetAvgConflictlengthCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15432
15433 case SCIP_VARSTATUS_MULTAGGR:
15434 return 0.0;
15435
15436 case SCIP_VARSTATUS_NEGATED:
15437 return SCIPvarGetAvgConflictlengthCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));
15438
15439 default:
15440 SCIPerrorMessage("unknown variable status\n");
15441 SCIPABORT();
15442 return 0.0; /*lint !e527*/
15443 }
15444 }
15445
15446 /** increases the number of branchings counter of the variable */
15447 SCIP_RETCODE SCIPvarIncNBranchings(
15448 SCIP_VAR* var, /**< problem variable */
15449 BMS_BLKMEM* blkmem, /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
15450 SCIP_SET* set, /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
15451 SCIP_STAT* stat, /**< problem statistics */
15452 SCIP_BRANCHDIR dir, /**< branching direction (downwards, or upwards) */
15453 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
15454 int depth /**< depth at which the bound change took place */
15455 )
15456 {
15457 assert(var != NULL);
15458 assert(stat != NULL);
15459 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15460
15461 /* check if history statistics should be collected for a variable */
15462 if( !stat->collectvarhistory )
15463 return SCIP_OKAY;
15464
15465 switch( SCIPvarGetStatus(var) )
15466 {
15467 case SCIP_VARSTATUS_ORIGINAL:
15468 if( var->data.original.transvar == NULL )
15469 {
15470 SCIPerrorMessage("cannot update branching counter of original untransformed variable\n");
15471 return SCIP_INVALIDDATA;
15472 }
15473 SCIP_CALL( SCIPvarIncNBranchings(var->data.original.transvar, blkmem, set, stat, dir, value, depth) );
15474 return SCIP_OKAY;
15475
15476 case SCIP_VARSTATUS_LOOSE:
15477 case SCIP_VARSTATUS_COLUMN:
15478 {
15479 SCIPhistoryIncNBranchings(var->history, dir, depth);
15480 SCIPhistoryIncNBranchings(var->historycrun, dir, depth);
15481 SCIPhistoryIncNBranchings(stat->glbhistory, dir, depth);
15482 SCIPhistoryIncNBranchings(stat->glbhistorycrun, dir, depth);
15483
15484 if( useValuehistory(var, value, set) )
15485 {
15486 SCIP_HISTORY* history;
15487
15488 SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
15489 assert(history != NULL);
15490
15491 SCIPhistoryIncNBranchings(history, dir, depth);
15492 }
15493
15494 return SCIP_OKAY;
15495 }
15496 case SCIP_VARSTATUS_FIXED:
15497 SCIPerrorMessage("cannot update branching counter of a fixed variable\n");
15498 return SCIP_INVALIDDATA;
15499
15500 case SCIP_VARSTATUS_AGGREGATED:
15501 value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;
15502
15503 if( var->data.aggregate.scalar > 0.0 )
15504 {
15505 SCIP_CALL( SCIPvarIncNBranchings(var->data.aggregate.var, blkmem, set, stat, dir, value, depth) );
15506 }
15507 else
15508 {
15509 assert(var->data.aggregate.scalar < 0.0);
15510 SCIP_CALL( SCIPvarIncNBranchings(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, depth) );
15511 }
15512 return SCIP_OKAY;
15513
15514 case SCIP_VARSTATUS_MULTAGGR:
15515 SCIPerrorMessage("cannot update branching counter of a multi-aggregated variable\n");
15516 return SCIP_INVALIDDATA;
15517
15518 case SCIP_VARSTATUS_NEGATED:
15519 value = 1.0 - value;
15520
15521 SCIP_CALL( SCIPvarIncNBranchings(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, depth) );
15522 return SCIP_OKAY;
15523
15524 default:
15525 SCIPerrorMessage("unknown variable status\n");
15526 return SCIP_INVALIDDATA;
15527 }
15528 }
15529
15530 /** increases the inference sum of the variable by the given weight */
15531 SCIP_RETCODE SCIPvarIncInferenceSum(
15532 SCIP_VAR* var, /**< problem variable */
15533 BMS_BLKMEM* blkmem, /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
15534 SCIP_SET* set, /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
15535 SCIP_STAT* stat, /**< problem statistics */
15536 SCIP_BRANCHDIR dir, /**< branching direction (downwards, or upwards) */
15537 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
15538 SCIP_Real weight /**< weight of this update in inference score */
15539 )
15540 {
15541 assert(var != NULL);
15542 assert(stat != NULL);
15543 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15544
15545 /* check if history statistics should be collected for a variable */
15546 if( !stat->collectvarhistory )
15547 return SCIP_OKAY;
15548
15549 switch( SCIPvarGetStatus(var) )
15550 {
15551 case SCIP_VARSTATUS_ORIGINAL:
15552 if( var->data.original.transvar == NULL )
15553 {
15554 SCIPerrorMessage("cannot update inference counter of original untransformed variable\n");
15555 return SCIP_INVALIDDATA;
15556 }
15557 SCIP_CALL( SCIPvarIncInferenceSum(var->data.original.transvar, blkmem, set, stat, dir, value, weight) );
15558 return SCIP_OKAY;
15559
15560 case SCIP_VARSTATUS_LOOSE:
15561 case SCIP_VARSTATUS_COLUMN:
15562 {
15563 SCIPhistoryIncInferenceSum(var->history, dir, weight);
15564 SCIPhistoryIncInferenceSum(var->historycrun, dir, weight);
15565 SCIPhistoryIncInferenceSum(stat->glbhistory, dir, weight);
15566 SCIPhistoryIncInferenceSum(stat->glbhistorycrun, dir, weight);
15567
15568 if( useValuehistory(var, value, set) )
15569 {
15570 SCIP_HISTORY* history;
15571
15572 SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
15573 assert(history != NULL);
15574
15575 SCIPhistoryIncInferenceSum(history, dir, weight);
15576 }
15577
15578 return SCIP_OKAY;
15579 }
15580 case SCIP_VARSTATUS_FIXED:
15581 SCIPerrorMessage("cannot update inference counter of a fixed variable\n");
15582 return SCIP_INVALIDDATA;
15583
15584 case SCIP_VARSTATUS_AGGREGATED:
15585 value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;
15586
15587 if( var->data.aggregate.scalar > 0.0 )
15588 {
15589 SCIP_CALL( SCIPvarIncInferenceSum(var->data.aggregate.var, blkmem, set, stat, dir, value, weight) );
15590 }
15591 else
15592 {
15593 assert(var->data.aggregate.scalar < 0.0);
15594 SCIP_CALL( SCIPvarIncInferenceSum(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
15595 }
15596 return SCIP_OKAY;
15597
15598 case SCIP_VARSTATUS_MULTAGGR:
15599 SCIPerrorMessage("cannot update inference counter of a multi-aggregated variable\n");
15600 return SCIP_INVALIDDATA;
15601
15602 case SCIP_VARSTATUS_NEGATED:
15603 value = 1.0 - value;
15604
15605 SCIP_CALL( SCIPvarIncInferenceSum(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
15606 return SCIP_OKAY;
15607
15608 default:
15609 SCIPerrorMessage("unknown variable status\n");
15610 return SCIP_INVALIDDATA;
15611 }
15612 }
15613
15614 /** increases the cutoff sum of the variable by the given weight */
15615 SCIP_RETCODE SCIPvarIncCutoffSum(
15616 SCIP_VAR* var, /**< problem variable */
15617 BMS_BLKMEM* blkmem, /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
15618 SCIP_SET* set, /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
15619 SCIP_STAT* stat, /**< problem statistics */
15620 SCIP_BRANCHDIR dir, /**< branching direction (downwards, or upwards) */
15621 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
15622 SCIP_Real weight /**< weight of this update in cutoff score */
15623 )
15624 {
15625 assert(var != NULL);
15626 assert(stat != NULL);
15627 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15628
15629 /* check if history statistics should be collected for a variable */
15630 if( !stat->collectvarhistory )
15631 return SCIP_OKAY;
15632
15633 switch( SCIPvarGetStatus(var) )
15634 {
15635 case SCIP_VARSTATUS_ORIGINAL:
15636 if( var->data.original.transvar == NULL )
15637 {
15638 SCIPerrorMessage("cannot update cutoff sum of original untransformed variable\n");
15639 return SCIP_INVALIDDATA;
15640 }
15641 SCIP_CALL( SCIPvarIncCutoffSum(var->data.original.transvar, blkmem, set, stat, dir, value, weight) );
15642 return SCIP_OKAY;
15643
15644 case SCIP_VARSTATUS_LOOSE:
15645 case SCIP_VARSTATUS_COLUMN:
15646 {
15647 SCIPhistoryIncCutoffSum(var->history, dir, weight);
15648 SCIPhistoryIncCutoffSum(var->historycrun, dir, weight);
15649 SCIPhistoryIncCutoffSum(stat->glbhistory, dir, weight);
15650 SCIPhistoryIncCutoffSum(stat->glbhistorycrun, dir, weight);
15651
15652 if( useValuehistory(var, value, set) )
15653 {
15654 SCIP_HISTORY* history;
15655
15656 SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
15657 assert(history != NULL);
15658
15659 SCIPhistoryIncCutoffSum(history, dir, weight);
15660 }
15661
15662 return SCIP_OKAY;
15663 }
15664 case SCIP_VARSTATUS_FIXED:
15665 SCIPerrorMessage("cannot update cutoff sum of a fixed variable\n");
15666 return SCIP_INVALIDDATA;
15667
15668 case SCIP_VARSTATUS_AGGREGATED:
15669 value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;
15670
15671 if( var->data.aggregate.scalar > 0.0 )
15672 {
15673 SCIP_CALL( SCIPvarIncCutoffSum(var->data.aggregate.var, blkmem, set, stat, dir, value, weight) );
15674 }
15675 else
15676 {
15677 assert(var->data.aggregate.scalar < 0.0);
15678 SCIP_CALL( SCIPvarIncCutoffSum(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
15679 }
15680 return SCIP_OKAY;
15681
15682 case SCIP_VARSTATUS_MULTAGGR:
15683 SCIPerrorMessage("cannot update cutoff sum of a multi-aggregated variable\n");
15684 return SCIP_INVALIDDATA;
15685
15686 case SCIP_VARSTATUS_NEGATED:
15687 value = 1.0 - value;
15688
15689 SCIP_CALL( SCIPvarIncCutoffSum(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
15690 return SCIP_OKAY;
15691
15692 default:
15693 SCIPerrorMessage("unknown variable status\n");
15694 return SCIP_INVALIDDATA;
15695 }
15696 }
15697
15698 /** returns the number of times, a bound of the variable was changed in given direction due to branching */
15699 SCIP_Longint SCIPvarGetNBranchings(
15700 SCIP_VAR* var, /**< problem variable */
15701 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15702 )
15703 {
15704 assert(var != NULL);
15705 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15706
15707 switch( SCIPvarGetStatus(var) )
15708 {
15709 case SCIP_VARSTATUS_ORIGINAL:
15710 if( var->data.original.transvar == NULL )
15711 return 0;
15712 else
15713 return SCIPvarGetNBranchings(var->data.original.transvar, dir);
15714
15715 case SCIP_VARSTATUS_LOOSE:
15716 case SCIP_VARSTATUS_COLUMN:
15717 return SCIPhistoryGetNBranchings(var->history, dir);
15718
15719 case SCIP_VARSTATUS_FIXED:
15720 return 0;
15721
15722 case SCIP_VARSTATUS_AGGREGATED:
15723 if( var->data.aggregate.scalar > 0.0 )
15724 return SCIPvarGetNBranchings(var->data.aggregate.var, dir);
15725 else
15726 return SCIPvarGetNBranchings(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15727
15728 case SCIP_VARSTATUS_MULTAGGR:
15729 return 0;
15730
15731 case SCIP_VARSTATUS_NEGATED:
15732 return SCIPvarGetNBranchings(var->negatedvar, SCIPbranchdirOpposite(dir));
15733
15734 default:
15735 SCIPerrorMessage("unknown variable status\n");
15736 SCIPABORT();
15737 return 0; /*lint !e527*/
15738 }
15739 }
15740
15741 /** returns the number of times, a bound of the variable was changed in given direction due to branching
15742 * in the current run
15743 */
15744 SCIP_Longint SCIPvarGetNBranchingsCurrentRun(
15745 SCIP_VAR* var, /**< problem variable */
15746 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15747 )
15748 {
15749 assert(var != NULL);
15750 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15751
15752 switch( SCIPvarGetStatus(var) )
15753 {
15754 case SCIP_VARSTATUS_ORIGINAL:
15755 if( var->data.original.transvar == NULL )
15756 return 0;
15757 else
15758 return SCIPvarGetNBranchingsCurrentRun(var->data.original.transvar, dir);
15759
15760 case SCIP_VARSTATUS_LOOSE:
15761 case SCIP_VARSTATUS_COLUMN:
15762 return SCIPhistoryGetNBranchings(var->historycrun, dir);
15763
15764 case SCIP_VARSTATUS_FIXED:
15765 return 0;
15766
15767 case SCIP_VARSTATUS_AGGREGATED:
15768 if( var->data.aggregate.scalar > 0.0 )
15769 return SCIPvarGetNBranchingsCurrentRun(var->data.aggregate.var, dir);
15770 else
15771 return SCIPvarGetNBranchingsCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15772
15773 case SCIP_VARSTATUS_MULTAGGR:
15774 return 0;
15775
15776 case SCIP_VARSTATUS_NEGATED:
15777 return SCIPvarGetNBranchingsCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));
15778
15779 default:
15780 SCIPerrorMessage("unknown variable status\n");
15781 SCIPABORT();
15782 return 0; /*lint !e527*/
15783 }
15784 }
15785
15786 /** returns the average depth of bound changes in given direction due to branching on the variable */
15787 SCIP_Real SCIPvarGetAvgBranchdepth(
15788 SCIP_VAR* var, /**< problem variable */
15789 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15790 )
15791 {
15792 assert(var != NULL);
15793 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15794
15795 switch( SCIPvarGetStatus(var) )
15796 {
15797 case SCIP_VARSTATUS_ORIGINAL:
15798 if( var->data.original.transvar == NULL )
15799 return 0.0;
15800 else
15801 return SCIPvarGetAvgBranchdepth(var->data.original.transvar, dir);
15802
15803 case SCIP_VARSTATUS_LOOSE:
15804 case SCIP_VARSTATUS_COLUMN:
15805 return SCIPhistoryGetAvgBranchdepth(var->history, dir);
15806
15807 case SCIP_VARSTATUS_FIXED:
15808 return 0.0;
15809
15810 case SCIP_VARSTATUS_AGGREGATED:
15811 if( var->data.aggregate.scalar > 0.0 )
15812 return SCIPvarGetAvgBranchdepth(var->data.aggregate.var, dir);
15813 else
15814 return SCIPvarGetAvgBranchdepth(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15815
15816 case SCIP_VARSTATUS_MULTAGGR:
15817 return 0.0;
15818
15819 case SCIP_VARSTATUS_NEGATED:
15820 return SCIPvarGetAvgBranchdepth(var->negatedvar, SCIPbranchdirOpposite(dir));
15821
15822 default:
15823 SCIPerrorMessage("unknown variable status\n");
15824 SCIPABORT();
15825 return 0.0; /*lint !e527*/
15826 }
15827 }
15828
15829 /** returns the average depth of bound changes in given direction due to branching on the variable
15830 * in the current run
15831 */
15832 SCIP_Real SCIPvarGetAvgBranchdepthCurrentRun(
15833 SCIP_VAR* var, /**< problem variable */
15834 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15835 )
15836 {
15837 assert(var != NULL);
15838 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15839
15840 switch( SCIPvarGetStatus(var) )
15841 {
15842 case SCIP_VARSTATUS_ORIGINAL:
15843 if( var->data.original.transvar == NULL )
15844 return 0.0;
15845 else
15846 return SCIPvarGetAvgBranchdepthCurrentRun(var->data.original.transvar, dir);
15847
15848 case SCIP_VARSTATUS_LOOSE:
15849 case SCIP_VARSTATUS_COLUMN:
15850 return SCIPhistoryGetAvgBranchdepth(var->historycrun, dir);
15851
15852 case SCIP_VARSTATUS_FIXED:
15853 return 0.0;
15854
15855 case SCIP_VARSTATUS_AGGREGATED:
15856 if( var->data.aggregate.scalar > 0.0 )
15857 return SCIPvarGetAvgBranchdepthCurrentRun(var->data.aggregate.var, dir);
15858 else
15859 return SCIPvarGetAvgBranchdepthCurrentRun(var->data.aggregate.var,
15860 dir == SCIP_BRANCHDIR_DOWNWARDS ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);
15861
15862 case SCIP_VARSTATUS_MULTAGGR:
15863 return 0.0;
15864
15865 case SCIP_VARSTATUS_NEGATED:
15866 return SCIPvarGetAvgBranchdepthCurrentRun(var->negatedvar,
15867 dir == SCIP_BRANCHDIR_DOWNWARDS ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);
15868
15869 default:
15870 SCIPerrorMessage("unknown variable status\n");
15871 SCIPABORT();
15872 return 0.0; /*lint !e527*/
15873 }
15874 }
15875
15876 /** returns the variable's VSIDS score */
15877 SCIP_Real SCIPvarGetVSIDS_rec(
15878 SCIP_VAR* var, /**< problem variable */
15879 SCIP_STAT* stat, /**< problem statistics */
15880 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15881 )
15882 {
15883 assert(var != NULL);
15884 assert(stat != NULL);
15885 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15886
15887 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
15888 return SCIPvarGetVSIDS(var->data.original.transvar, stat, dir);
15889
15890 switch( SCIPvarGetStatus(var) )
15891 {
15892 case SCIP_VARSTATUS_ORIGINAL:
15893 if( var->data.original.transvar == NULL )
15894 return 0.0;
15895 else
15896 return SCIPvarGetVSIDS(var->data.original.transvar, stat, dir);
15897
15898 case SCIP_VARSTATUS_LOOSE:
15899 case SCIP_VARSTATUS_COLUMN:
15900 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE); /* column case already handled in if condition above */
15901 return SCIPhistoryGetVSIDS(var->history, dir)/stat->vsidsweight;
15902
15903 case SCIP_VARSTATUS_FIXED:
15904 return 0.0;
15905
15906 case SCIP_VARSTATUS_AGGREGATED:
15907 if( var->data.aggregate.scalar > 0.0 )
15908 return SCIPvarGetVSIDS(var->data.aggregate.var, stat, dir);
15909 else
15910 /* coverity[overrun-local] */
15911 return SCIPvarGetVSIDS(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
15912
15913 case SCIP_VARSTATUS_MULTAGGR:
15914 return 0.0;
15915
15916 case SCIP_VARSTATUS_NEGATED:
15917 /* coverity[overrun-local] */
15918 return SCIPvarGetVSIDS(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
15919
15920 default:
15921 SCIPerrorMessage("unknown variable status\n");
15922 SCIPABORT();
15923 return 0.0; /*lint !e527*/
15924 }
15925 }
15926
15927 /** returns the variable's VSIDS score only using conflicts of the current run */
15928 SCIP_Real SCIPvarGetVSIDSCurrentRun(
15929 SCIP_VAR* var, /**< problem variable */
15930 SCIP_STAT* stat, /**< problem statistics */
15931 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15932 )
15933 {
15934 assert(var != NULL);
15935 assert(stat != NULL);
15936 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15937
15938 if( dir != SCIP_BRANCHDIR_DOWNWARDS && dir != SCIP_BRANCHDIR_UPWARDS )
15939 {
15940 SCIPerrorMessage("invalid branching direction %d when asking for VSIDS value\n", dir);
15941 return SCIP_INVALID;
15942 }
15943
15944 switch( SCIPvarGetStatus(var) )
15945 {
15946 case SCIP_VARSTATUS_ORIGINAL:
15947 if( var->data.original.transvar == NULL )
15948 return 0.0;
15949 else
15950 return SCIPvarGetVSIDSCurrentRun(var->data.original.transvar, stat, dir);
15951
15952 case SCIP_VARSTATUS_LOOSE:
15953 case SCIP_VARSTATUS_COLUMN:
15954 return SCIPhistoryGetVSIDS(var->historycrun, dir)/stat->vsidsweight;
15955
15956 case SCIP_VARSTATUS_FIXED:
15957 return 0.0;
15958
15959 case SCIP_VARSTATUS_AGGREGATED:
15960 if( var->data.aggregate.scalar > 0.0 )
15961 return SCIPvarGetVSIDSCurrentRun(var->data.aggregate.var, stat, dir);
15962 else
15963 return SCIPvarGetVSIDSCurrentRun(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
15964
15965 case SCIP_VARSTATUS_MULTAGGR:
15966 return 0.0;
15967
15968 case SCIP_VARSTATUS_NEGATED:
15969 return SCIPvarGetVSIDSCurrentRun(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
15970
15971 default:
15972 SCIPerrorMessage("unknown variable status\n");
15973 SCIPABORT();
15974 return 0.0; /*lint !e527*/
15975 }
15976 }
15977
15978 /** returns the number of inferences branching on this variable in given direction triggered */
15979 SCIP_Real SCIPvarGetInferenceSum(
15980 SCIP_VAR* var, /**< problem variable */
15981 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15982 )
15983 {
15984 assert(var != NULL);
15985 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15986
15987 switch( SCIPvarGetStatus(var) )
15988 {
15989 case SCIP_VARSTATUS_ORIGINAL:
15990 if( var->data.original.transvar == NULL )
15991 return 0.0;
15992 else
15993 return SCIPvarGetInferenceSum(var->data.original.transvar, dir);
15994
15995 case SCIP_VARSTATUS_LOOSE:
15996 case SCIP_VARSTATUS_COLUMN:
15997 return SCIPhistoryGetInferenceSum(var->history, dir);
15998
15999 case SCIP_VARSTATUS_FIXED:
16000 return 0.0;
16001
16002 case SCIP_VARSTATUS_AGGREGATED:
16003 if( var->data.aggregate.scalar > 0.0 )
16004 return SCIPvarGetInferenceSum(var->data.aggregate.var, dir);
16005 else
16006 return SCIPvarGetInferenceSum(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
16007
16008 case SCIP_VARSTATUS_MULTAGGR:
16009 return 0.0;
16010
16011 case SCIP_VARSTATUS_NEGATED:
16012 return SCIPvarGetInferenceSum(var->negatedvar, SCIPbranchdirOpposite(dir));
16013
16014 default:
16015 SCIPerrorMessage("unknown variable status\n");
16016 SCIPABORT();
16017 return 0.0; /*lint !e527*/
16018 }
16019 }
16020
16021 /** returns the number of inferences branching on this variable in given direction triggered
16022 * in the current run
16023 */
16024 SCIP_Real SCIPvarGetInferenceSumCurrentRun(
16025 SCIP_VAR* var, /**< problem variable */
16026 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
16027 )
16028 {
16029 assert(var != NULL);
16030 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
16031
16032 switch( SCIPvarGetStatus(var) )
16033 {
16034 case SCIP_VARSTATUS_ORIGINAL:
16035 if( var->data.original.transvar == NULL )
16036 return 0.0;
16037 else
16038 return SCIPvarGetInferenceSumCurrentRun(var->data.original.transvar, dir);
16039
16040 case SCIP_VARSTATUS_LOOSE:
16041 case SCIP_VARSTATUS_COLUMN:
16042 return SCIPhistoryGetInferenceSum(var->historycrun, dir);
16043
16044 case SCIP_VARSTATUS_FIXED:
16045 return 0.0;
16046
16047 case SCIP_VARSTATUS_AGGREGATED:
16048 if( var->data.aggregate.scalar > 0.0 )
16049 return SCIPvarGetInferenceSumCurrentRun(var->data.aggregate.var, dir);
16050 else
16051 return SCIPvarGetInferenceSumCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
16052
16053 case SCIP_VARSTATUS_MULTAGGR:
16054 return 0.0;
16055
16056 case SCIP_VARSTATUS_NEGATED:
16057 return SCIPvarGetInferenceSumCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));
16058
16059 default:
16060 SCIPerrorMessage("unknown variable status\n");
16061 SCIPABORT();
16062 return 0.0; /*lint !e527*/
16063 }
16064 }
16065
16066 /** returns the average number of inferences found after branching on the variable in given direction */
16067 SCIP_Real SCIPvarGetAvgInferences(
16068 SCIP_VAR* var, /**< problem variable */
16069 SCIP_STAT* stat, /**< problem statistics */
16070 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
16071 )
16072 {
16073 assert(var != NULL);
16074 assert(stat != NULL);
16075 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
16076
16077 switch( SCIPvarGetStatus(var) )
16078 {
16079 case SCIP_VARSTATUS_ORIGINAL:
16080 if( var->data.original.transvar == NULL )
16081 return SCIPhistoryGetAvgInferences(stat->glbhistory, dir);
16082 else
16083 return SCIPvarGetAvgInferences(var->data.original.transvar, stat, dir);
16084
16085 case SCIP_VARSTATUS_LOOSE:
16086 case SCIP_VARSTATUS_COLUMN:
16087 if( SCIPhistoryGetNBranchings(var->history, dir) > 0 )
16088 return SCIPhistoryGetAvgInferences(var->history, dir);
16089 else
16090 {
16091 int nimpls;
16092 int ncliques;
16093
16094 nimpls = SCIPvarGetNImpls(var, dir == SCIP_BRANCHDIR_UPWARDS);
16095 ncliques = SCIPvarGetNCliques(var, dir == SCIP_BRANCHDIR_UPWARDS);
16096 return nimpls + ncliques > 0 ? (SCIP_Real)(nimpls + 2*ncliques) : SCIPhistoryGetAvgInferences(stat->glbhistory, dir); /*lint !e790*/
16097 }
16098
16099 case SCIP_VARSTATUS_FIXED:
16100 return 0.0;
16101
16102 case SCIP_VARSTATUS_AGGREGATED:
16103 if( var->data.aggregate.scalar > 0.0 )
16104 return SCIPvarGetAvgInferences(var->data.aggregate.var, stat, dir);
16105 else
16106 return SCIPvarGetAvgInferences(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
16107
16108 case SCIP_VARSTATUS_MULTAGGR:
16109 return 0.0;
16110
16111 case SCIP_VARSTATUS_NEGATED:
16112 return SCIPvarGetAvgInferences(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
16113
16114 default:
16115 SCIPerrorMessage("unknown variable status\n");
16116 SCIPABORT();
16117 return 0.0; /*lint !e527*/
16118 }
16119 }
16120
16121 /** returns the average number of inferences found after branching on the variable in given direction
16122 * in the current run
16123 */
16124 SCIP_Real SCIPvarGetAvgInferencesCurrentRun(
16125 SCIP_VAR* var, /**< problem variable */
16126 SCIP_STAT* stat, /**< problem statistics */
16127 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
16128 )
16129 {
16130 assert(var != NULL);
16131 assert(stat != NULL);
16132 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
16133
16134 switch( SCIPvarGetStatus(var) )
16135 {
16136 case SCIP_VARSTATUS_ORIGINAL:
16137 if( var->data.original.transvar == NULL )
16138 return SCIPhistoryGetAvgInferences(stat->glbhistorycrun, dir);
16139 else
16140 return SCIPvarGetAvgInferencesCurrentRun(var->data.original.transvar, stat, dir);
16141
16142 case SCIP_VARSTATUS_LOOSE:
16143 case SCIP_VARSTATUS_COLUMN:
16144 if( SCIPhistoryGetNBranchings(var->historycrun, dir) > 0 )
16145 return SCIPhistoryGetAvgInferences(var->historycrun, dir);
16146 else
16147 {
16148 int nimpls;
16149 int ncliques;
16150
16151 nimpls = SCIPvarGetNImpls(var, dir == SCIP_BRANCHDIR_UPWARDS);
16152 ncliques = SCIPvarGetNCliques(var, dir == SCIP_BRANCHDIR_UPWARDS);
16153 return nimpls + ncliques > 0 ? (SCIP_Real)(nimpls + 2*ncliques) : SCIPhistoryGetAvgInferences(stat->glbhistorycrun, dir); /*lint !e790*/
16154 }
16155
16156 case SCIP_VARSTATUS_FIXED:
16157 return 0.0;
16158
16159 case SCIP_VARSTATUS_AGGREGATED:
16160 if( var->data.aggregate.scalar > 0.0 )
16161 return SCIPvarGetAvgInferencesCurrentRun(var->data.aggregate.var, stat, dir);
16162 else
16163 return SCIPvarGetAvgInferencesCurrentRun(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
16164
16165 case SCIP_VARSTATUS_MULTAGGR:
16166 return 0.0;
16167
16168 case SCIP_VARSTATUS_NEGATED:
16169 return SCIPvarGetAvgInferencesCurrentRun(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
16170
16171 default:
16172 SCIPerrorMessage("unknown variable status\n");
16173 SCIPABORT();
16174 return 0.0; /*lint !e527*/
16175 }
16176 }
16177
16178 /** returns the number of cutoffs branching on this variable in given direction produced */
16179 SCIP_Real SCIPvarGetCutoffSum(
16180 SCIP_VAR* var, /**< problem variable */
16181 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
16182 )
16183 {
16184 assert(var != NULL);
16185 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
16186
16187 switch( SCIPvarGetStatus(var) )
16188 {
16189 case SCIP_VARSTATUS_ORIGINAL:
16190 if( var->data.original.transvar == NULL )
16191 return 0;
16192 else
16193 return SCIPvarGetCutoffSum(var->data.original.transvar, dir);
16194
16195 case SCIP_VARSTATUS_LOOSE:
16196 case SCIP_VARSTATUS_COLUMN:
16197 return SCIPhistoryGetCutoffSum(var->history, dir);
16198
16199 case SCIP_VARSTATUS_FIXED:
16200 return 0;
16201
16202 case SCIP_VARSTATUS_AGGREGATED:
16203 if( var->data.aggregate.scalar > 0.0 )
16204 return SCIPvarGetCutoffSum(var->data.aggregate.var, dir);
16205 else
16206 return SCIPvarGetCutoffSum(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
16207
16208 case SCIP_VARSTATUS_MULTAGGR:
16209 return 0;
16210
16211 case SCIP_VARSTATUS_NEGATED:
16212 return SCIPvarGetCutoffSum(var->negatedvar, SCIPbranchdirOpposite(dir));
16213
16214 default:
16215 SCIPerrorMessage("unknown variable status\n");
16216 SCIPABORT();
16217 return 0; /*lint !e527*/
16218 }
16219 }
16220
16221 /** returns the number of cutoffs branching on this variable in given direction produced in the current run */
16222 SCIP_Real SCIPvarGetCutoffSumCurrentRun(
16223 SCIP_VAR* var, /**< problem variable */
16224 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
16225 )
16226 {
16227 assert(var != NULL);
16228 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
16229
16230 switch( SCIPvarGetStatus(var) )
16231 {
16232 case SCIP_VARSTATUS_ORIGINAL:
16233 if( var->data.original.transvar == NULL )
16234 return 0;
16235 else
16236 return SCIPvarGetCutoffSumCurrentRun(var->data.original.transvar, dir);
16237
16238 case SCIP_VARSTATUS_LOOSE:
16239 case SCIP_VARSTATUS_COLUMN:
16240 return SCIPhistoryGetCutoffSum(var->historycrun, dir);
16241
16242 case SCIP_VARSTATUS_FIXED:
16243 return 0;
16244
16245 case SCIP_VARSTATUS_AGGREGATED:
16246 if( var->data.aggregate.scalar > 0.0 )
16247 return SCIPvarGetCutoffSumCurrentRun(var->data.aggregate.var, dir);
16248 else
16249 return SCIPvarGetCutoffSumCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
16250
16251 case SCIP_VARSTATUS_MULTAGGR:
16252 return 0;
16253
16254 case SCIP_VARSTATUS_NEGATED:
16255 return SCIPvarGetCutoffSumCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));
16256
16257 default:
16258 SCIPerrorMessage("unknown variable status\n");
16259 SCIPABORT();
16260 return 0; /*lint !e527*/
16261 }
16262 }
16263
16264 /** returns the average number of cutoffs found after branching on the variable in given direction */
16265 SCIP_Real SCIPvarGetAvgCutoffs(
16266 SCIP_VAR* var, /**< problem variable */
16267 SCIP_STAT* stat, /**< problem statistics */
16268 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
16269 )
16270 {
16271 assert(var != NULL);
16272 assert(stat != NULL);
16273 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
16274
16275 switch( SCIPvarGetStatus(var) )
16276 {
16277 case SCIP_VARSTATUS_ORIGINAL:
16278 if( var->data.original.transvar == NULL )
16279 return SCIPhistoryGetAvgCutoffs(stat->glbhistory, dir);
16280 else
16281 return SCIPvarGetAvgCutoffs(var->data.original.transvar, stat, dir);
16282
16283 case SCIP_VARSTATUS_LOOSE:
16284 case SCIP_VARSTATUS_COLUMN:
16285 return SCIPhistoryGetNBranchings(var->history, dir) > 0
16286 ? SCIPhistoryGetAvgCutoffs(var->history, dir)
16287 : SCIPhistoryGetAvgCutoffs(stat->glbhistory, dir);
16288
16289 case SCIP_VARSTATUS_FIXED:
16290 return 0.0;
16291
16292 case SCIP_VARSTATUS_AGGREGATED:
16293 if( var->data.aggregate.scalar > 0.0 )
16294 return SCIPvarGetAvgCutoffs(var->data.aggregate.var, stat, dir);
16295 else
16296 return SCIPvarGetAvgCutoffs(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
16297
16298 case SCIP_VARSTATUS_MULTAGGR:
16299 return 0.0;
16300
16301 case SCIP_VARSTATUS_NEGATED:
16302 return SCIPvarGetAvgCutoffs(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
16303
16304 default:
16305 SCIPerrorMessage("unknown variable status\n");
16306 SCIPABORT();
16307 return 0.0; /*lint !e527*/
16308 }
16309 }
16310
16311 /** returns the average number of cutoffs found after branching on the variable in given direction in the current run */
16312 SCIP_Real SCIPvarGetAvgCutoffsCurrentRun(
16313 SCIP_VAR* var, /**< problem variable */
16314 SCIP_STAT* stat, /**< problem statistics */
16315 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
16316 )
16317 {
16318 assert(var != NULL);
16319 assert(stat != NULL);
16320 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
16321
16322 switch( SCIPvarGetStatus(var) )
16323 {
16324 case SCIP_VARSTATUS_ORIGINAL:
16325 if( var->data.original.transvar == NULL )
16326 return SCIPhistoryGetAvgCutoffs(stat->glbhistorycrun, dir);
16327 else
16328 return SCIPvarGetAvgCutoffsCurrentRun(var->data.original.transvar, stat, dir);
16329
16330 case SCIP_VARSTATUS_LOOSE:
16331 case SCIP_VARSTATUS_COLUMN:
16332 return SCIPhistoryGetNBranchings(var->historycrun, dir) > 0
16333 ? SCIPhistoryGetAvgCutoffs(var->historycrun, dir)
16334 : SCIPhistoryGetAvgCutoffs(stat->glbhistorycrun, dir);
16335
16336 case SCIP_VARSTATUS_FIXED:
16337 return 0.0;
16338
16339 case SCIP_VARSTATUS_AGGREGATED:
16340 if( var->data.aggregate.scalar > 0.0 )
16341 return SCIPvarGetAvgCutoffsCurrentRun(var->data.aggregate.var, stat, dir);
16342 else
16343 return SCIPvarGetAvgCutoffsCurrentRun(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
16344
16345 case SCIP_VARSTATUS_MULTAGGR:
16346 return 0.0;
16347
16348 case SCIP_VARSTATUS_NEGATED:
16349 return SCIPvarGetAvgCutoffsCurrentRun(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
16350
16351 default:
16352 SCIPerrorMessage("unknown variable status\n");
16353 SCIPABORT();
16354 return 0.0; /*lint !e527*/
16355 }
16356 }
16357
16358 /** returns the variable's average GMI efficacy score value generated from simplex tableau rows of this variable */
16359 SCIP_Real SCIPvarGetAvgGMIScore(
16360 SCIP_VAR* var, /**< problem variable */
16361 SCIP_STAT* stat /**< problem statistics */
16362 )
16363 {
16364 assert(var != NULL);
16365 assert(stat != NULL);
16366
16367 switch( SCIPvarGetStatus(var) )
16368 {
16369 case SCIP_VARSTATUS_ORIGINAL:
16370 if( var->data.original.transvar == NULL )
16371 return 0.0;
16372 else
16373 return SCIPvarGetAvgGMIScore(var->data.original.transvar, stat);
16374
16375 case SCIP_VARSTATUS_LOOSE:
16376 case SCIP_VARSTATUS_COLUMN:
16377 return SCIPhistoryGetAvgGMIeff(var->history);
16378
16379 case SCIP_VARSTATUS_FIXED:
16380 return 0.0;
16381
16382 case SCIP_VARSTATUS_AGGREGATED:
16383 return SCIPvarGetAvgGMIScore(var->data.aggregate.var, stat);
16384
16385 case SCIP_VARSTATUS_MULTAGGR:
16386 return 0.0;
16387
16388 case SCIP_VARSTATUS_NEGATED:
16389 return SCIPvarGetAvgGMIScore(var->negatedvar, stat);
16390
16391 default:
16392 SCIPerrorMessage("unknown variable status\n");
16393 SCIPABORT();
16394 return 0.0; /*lint !e527*/
16395 }
16396 }
16397
16398 /** increase the variable's GMI efficacy scores generated from simplex tableau rows of this variable */
16399 SCIP_RETCODE SCIPvarIncGMIeffSum(
16400 SCIP_VAR* var, /**< problem variable */
16401 SCIP_STAT* stat, /**< problem statistics */
16402 SCIP_Real gmieff /**< efficacy of last GMI cut produced when variable was frac and basic */
16403 )
16404 {
16405 assert(var != NULL);
16406 assert(stat != NULL);
16407 assert(gmieff >= 0);
16408
16409 switch( SCIPvarGetStatus(var) )
16410 {
16411 case SCIP_VARSTATUS_ORIGINAL:
16412 if( var->data.original.transvar != NULL )
16413 SCIP_CALL( SCIPvarIncGMIeffSum(var->data.original.transvar, stat, gmieff) );
16414 return SCIP_OKAY;
16415
16416 case SCIP_VARSTATUS_LOOSE:
16417 case SCIP_VARSTATUS_COLUMN:
16418 SCIPhistoryIncGMIeffSum(var->history, gmieff);
16419 return SCIP_OKAY;
16420
16421 case SCIP_VARSTATUS_FIXED:
16422 return SCIP_INVALIDDATA;
16423
16424 case SCIP_VARSTATUS_AGGREGATED:
16425 SCIP_CALL( SCIPvarIncGMIeffSum(var->data.aggregate.var, stat, gmieff) );
16426 return SCIP_OKAY;
16427
16428 case SCIP_VARSTATUS_NEGATED:
16429 SCIP_CALL( SCIPvarIncGMIeffSum(var->negatedvar, stat, gmieff) );
16430 return SCIP_OKAY;
16431
16432 case SCIP_VARSTATUS_MULTAGGR:
16433 return SCIP_INVALIDDATA;
16434
16435 default:
16436 SCIPerrorMessage("unknown variable status\n");
16437 SCIPABORT();
16438 return SCIP_INVALIDDATA; /*lint !e527*/
16439 }
16440 }
16441
16442 /** returns the variable's last GMI efficacy score value generated from a simplex tableau row of this variable */
16443 SCIP_Real SCIPvarGetLastGMIScore(
16444 SCIP_VAR* var, /**< problem variable */
16445 SCIP_STAT* stat /**< problem statistics */
16446 )
16447 {
16448 assert(var != NULL);
16449 assert(stat != NULL);
16450
16451 switch( SCIPvarGetStatus(var) )
16452 {
16453 case SCIP_VARSTATUS_ORIGINAL:
16454 if( var->data.original.transvar != NULL )
16455 return SCIPvarGetLastGMIScore(var->data.original.transvar, stat);
16456 return 0.0;
16457
16458 case SCIP_VARSTATUS_LOOSE:
16459 case SCIP_VARSTATUS_COLUMN:
16460 return SCIPhistoryGetLastGMIeff(var->history);
16461
16462 case SCIP_VARSTATUS_FIXED:
16463 return 0.0;
16464
16465 case SCIP_VARSTATUS_AGGREGATED:
16466 return SCIPvarGetLastGMIScore(var->data.aggregate.var, stat);
16467
16468 case SCIP_VARSTATUS_MULTAGGR:
16469 return 0.0;
16470
16471 case SCIP_VARSTATUS_NEGATED:
16472 return SCIPvarGetLastGMIScore(var->negatedvar, stat);
16473
16474 default:
16475 SCIPerrorMessage("unknown variable status\n");
16476 SCIPABORT();
16477 return 0.0; /*lint !e527*/
16478 }
16479 }
16480
16481
16482 /** sets the variable's last GMI efficacy score value generated from a simplex tableau row of this variable */
16483 SCIP_RETCODE SCIPvarSetLastGMIScore(
16484 SCIP_VAR* var, /**< problem variable */
16485 SCIP_STAT* stat, /**< problem statistics */
16486 SCIP_Real gmieff /**< efficacy of last GMI cut produced when variable was frac and basic */
16487 )
16488 {
16489 assert(var != NULL);
16490 assert(stat != NULL);
16491 assert(gmieff >= 0);
16492
16493 switch( SCIPvarGetStatus(var) )
16494 {
16495 case SCIP_VARSTATUS_ORIGINAL:
16496 if( var->data.original.transvar != NULL )
16497 SCIP_CALL( SCIPvarSetLastGMIScore(var->data.original.transvar, stat, gmieff) );
16498 return SCIP_OKAY;
16499
16500 case SCIP_VARSTATUS_LOOSE:
16501 case SCIP_VARSTATUS_COLUMN:
16502 SCIPhistorySetLastGMIeff(var->history, gmieff);
16503 return SCIP_OKAY;
16504
16505 case SCIP_VARSTATUS_FIXED:
16506 return SCIP_INVALIDDATA;
16507
16508 case SCIP_VARSTATUS_AGGREGATED:
16509 SCIP_CALL( SCIPvarSetLastGMIScore(var->data.aggregate.var, stat, gmieff) );
16510 return SCIP_OKAY;
16511
16512 case SCIP_VARSTATUS_NEGATED:
16513 SCIP_CALL( SCIPvarSetLastGMIScore(var->negatedvar, stat, gmieff) );
16514 return SCIP_OKAY;
16515
16516 case SCIP_VARSTATUS_MULTAGGR:
16517 return SCIP_INVALIDDATA;
16518
16519 default:
16520 SCIPerrorMessage("unknown variable status\n");
16521 SCIPABORT();
16522 return SCIP_INVALIDDATA; /*lint !e527*/
16523 }
16524 }
16525
16526
16527
16528 /*
16529 * information methods for bound changes
16530 */
16531
16532 /** creates an artificial bound change information object with depth = INT_MAX and pos = -1 */
16533 SCIP_RETCODE SCIPbdchginfoCreate(
16534 SCIP_BDCHGINFO** bdchginfo, /**< pointer to store bound change information */
16535 BMS_BLKMEM* blkmem, /**< block memory */
16536 SCIP_VAR* var, /**< active variable that changed the bounds */
16537 SCIP_BOUNDTYPE boundtype, /**< type of bound for var: lower or upper bound */
16538 SCIP_Real oldbound, /**< old value for bound */
16539 SCIP_Real newbound /**< new value for bound */
16540 )
16541 {
16542 assert(bdchginfo != NULL);
16543
16544 SCIP_ALLOC( BMSallocBlockMemory(blkmem, bdchginfo) );
16545 (*bdchginfo)->oldbound = oldbound;
16546 (*bdchginfo)->newbound = newbound;
16547 (*bdchginfo)->var = var;
16548 (*bdchginfo)->inferencedata.var = var;
16549 (*bdchginfo)->inferencedata.reason.prop = NULL;
16550 (*bdchginfo)->inferencedata.info = 0;
16551 (*bdchginfo)->bdchgidx.depth = INT_MAX;
16552 (*bdchginfo)->bdchgidx.pos = -1;
16553 (*bdchginfo)->pos = 0;
16554 (*bdchginfo)->boundchgtype = SCIP_BOUNDCHGTYPE_BRANCHING; /*lint !e641*/
16555 (*bdchginfo)->boundtype = boundtype; /*lint !e641*/
16556 (*bdchginfo)->inferboundtype = boundtype; /*lint !e641*/
16557 (*bdchginfo)->redundant = FALSE;
16558
16559 return SCIP_OKAY;
16560 }
16561
16562 /** frees a bound change information object */
16563 void SCIPbdchginfoFree(
16564 SCIP_BDCHGINFO** bdchginfo, /**< pointer to store bound change information */
16565 BMS_BLKMEM* blkmem /**< block memory */
16566 )
16567 {
16568 assert(bdchginfo != NULL);
16569
16570 BMSfreeBlockMemory(blkmem, bdchginfo);
16571 }
16572
16573 /** returns the bound change information for the last lower bound change on given active problem variable before or
16574 * after the bound change with the given index was applied;
16575 * returns NULL, if no change to the lower bound was applied up to this point of time
16576 */
16577 SCIP_BDCHGINFO* SCIPvarGetLbchgInfo(
16578 SCIP_VAR* var, /**< active problem variable */
16579 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16580 SCIP_Bool after /**< should the bound change with given index be included? */
16581 )
16582 {
16583 int i;
16584
16585 assert(var != NULL);
16586 assert(SCIPvarIsActive(var));
16587
16588 /* search the correct bound change information for the given bound change index */
16589 if( after )
16590 {
16591 for( i = var->nlbchginfos-1; i >= 0; --i )
16592 {
16593 assert(var->lbchginfos[i].var == var);
16594 assert((SCIP_BOUNDTYPE)var->lbchginfos[i].boundtype == SCIP_BOUNDTYPE_LOWER);
16595 assert(var->lbchginfos[i].pos == i);
16596
16597 /* if we reached the (due to global bounds) redundant bound changes, return NULL */
16598 if( var->lbchginfos[i].redundant )
16599 return NULL;
16600 assert(var->lbchginfos[i].oldbound < var->lbchginfos[i].newbound);
16601
16602 /* if we reached the bound change index, return the current bound change info */
16603 if( !SCIPbdchgidxIsEarlier(bdchgidx, &var->lbchginfos[i].bdchgidx) )
16604 return &var->lbchginfos[i];
16605 }
16606 }
16607 else
16608 {
16609 for( i = var->nlbchginfos-1; i >= 0; --i )
16610 {
16611 assert(var->lbchginfos[i].var == var);
16612 assert((SCIP_BOUNDTYPE)var->lbchginfos[i].boundtype == SCIP_BOUNDTYPE_LOWER);
16613 assert(var->lbchginfos[i].pos == i);
16614
16615 /* if we reached the (due to global bounds) redundant bound changes, return NULL */
16616 if( var->lbchginfos[i].redundant )
16617 return NULL;
16618 assert(var->lbchginfos[i].oldbound < var->lbchginfos[i].newbound);
16619
16620 /* if we reached the bound change index, return the current bound change info */
16621 if( SCIPbdchgidxIsEarlier(&var->lbchginfos[i].bdchgidx, bdchgidx) )
16622 return &var->lbchginfos[i];
16623 }
16624 }
16625
16626 return NULL;
16627 }
16628
16629 /** returns the bound change information for the last upper bound change on given active problem variable before or
16630 * after the bound change with the given index was applied;
16631 * returns NULL, if no change to the upper bound was applied up to this point of time
16632 */
16633 SCIP_BDCHGINFO* SCIPvarGetUbchgInfo(
16634 SCIP_VAR* var, /**< active problem variable */
16635 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16636 SCIP_Bool after /**< should the bound change with given index be included? */
16637 )
16638 {
16639 int i;
16640
16641 assert(var != NULL);
16642 assert(SCIPvarIsActive(var));
16643
16644 /* search the correct bound change information for the given bound change index */
16645 if( after )
16646 {
16647 for( i = var->nubchginfos-1; i >= 0; --i )
16648 {
16649 assert(var->ubchginfos[i].var == var);
16650 assert((SCIP_BOUNDTYPE)var->ubchginfos[i].boundtype == SCIP_BOUNDTYPE_UPPER);
16651 assert(var->ubchginfos[i].pos == i);
16652
16653 /* if we reached the (due to global bounds) redundant bound changes, return NULL */
16654 if( var->ubchginfos[i].redundant )
16655 return NULL;
16656 assert(var->ubchginfos[i].oldbound > var->ubchginfos[i].newbound);
16657
16658 /* if we reached the bound change index, return the current bound change info */
16659 if( !SCIPbdchgidxIsEarlier(bdchgidx, &var->ubchginfos[i].bdchgidx) )
16660 return &var->ubchginfos[i];
16661 }
16662 }
16663 else
16664 {
16665 for( i = var->nubchginfos-1; i >= 0; --i )
16666 {
16667 assert(var->ubchginfos[i].var == var);
16668 assert((SCIP_BOUNDTYPE)var->ubchginfos[i].boundtype == SCIP_BOUNDTYPE_UPPER);
16669 assert(var->ubchginfos[i].pos == i);
16670
16671 /* if we reached the (due to global bounds) redundant bound changes, return NULL */
16672 if( var->ubchginfos[i].redundant )
16673 return NULL;
16674 assert(var->ubchginfos[i].oldbound > var->ubchginfos[i].newbound);
16675
16676 /* if we reached the bound change index, return the current bound change info */
16677 if( SCIPbdchgidxIsEarlier(&var->ubchginfos[i].bdchgidx, bdchgidx) )
16678 return &var->ubchginfos[i];
16679 }
16680 }
16681
16682 return NULL;
16683 }
16684
16685 /** returns the bound change information for the last lower or upper bound change on given active problem variable
16686 * before or after the bound change with the given index was applied;
16687 * returns NULL, if no change to the lower/upper bound was applied up to this point of time
16688 */
16689 SCIP_BDCHGINFO* SCIPvarGetBdchgInfo(
16690 SCIP_VAR* var, /**< active problem variable */
16691 SCIP_BOUNDTYPE boundtype, /**< type of bound: lower or upper bound */
16692 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16693 SCIP_Bool after /**< should the bound change with given index be included? */
16694 )
16695 {
16696 if( boundtype == SCIP_BOUNDTYPE_LOWER )
16697 return SCIPvarGetLbchgInfo(var, bdchgidx, after);
16698 else
16699 {
16700 assert(boundtype == SCIP_BOUNDTYPE_UPPER);
16701 return SCIPvarGetUbchgInfo(var, bdchgidx, after);
16702 }
16703 }
16704
16705 /** returns lower bound of variable directly before or after the bound change given by the bound change index
16706 * was applied
16707 *
16708 * @deprecated Please use SCIPgetVarLbAtIndex()
16709 */
16710 SCIP_Real SCIPvarGetLbAtIndex(
16711 SCIP_VAR* var, /**< problem variable */
16712 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16713 SCIP_Bool after /**< should the bound change with given index be included? */
16714 )
16715 {
16716 SCIP_VARSTATUS varstatus;
16717 assert(var != NULL);
16718
16719 varstatus = SCIPvarGetStatus(var);
16720
16721 /* get bounds of attached variables */
16722 switch( varstatus )
16723 {
16724 case SCIP_VARSTATUS_ORIGINAL:
16725 assert(var->data.original.transvar != NULL);
16726 return SCIPvarGetLbAtIndex(var->data.original.transvar, bdchgidx, after);
16727
16728 case SCIP_VARSTATUS_LOOSE:
16729 case SCIP_VARSTATUS_COLUMN:
16730 if( bdchgidx == NULL )
16731 return SCIPvarGetLbLocal(var);
16732 else
16733 {
16734 SCIP_BDCHGINFO* bdchginfo;
16735
16736 bdchginfo = SCIPvarGetLbchgInfo(var, bdchgidx, after);
16737 if( bdchginfo != NULL )
16738 return SCIPbdchginfoGetNewbound(bdchginfo);
16739 else
16740 return var->glbdom.lb;
16741 }
16742 case SCIP_VARSTATUS_FIXED:
16743 return var->glbdom.lb;
16744
16745 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
16746 assert(var->data.aggregate.var != NULL);
16747 /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
16748 * corresponding infinity value instead of performing an arithmetical transformation (compare method
16749 * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
16750 * (or is called by) a public interface method; instead, we only assert that values are finite
16751 * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
16752 * positives and negatives if the parameter <numerics/infinity> is modified by the user
16753 */
16754 if( var->data.aggregate.scalar > 0.0 )
16755 {
16756 /* a > 0 -> get lower bound of y */
16757 assert(SCIPvarGetLbAtIndex(var->data.aggregate.var, bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16758 assert(SCIPvarGetLbAtIndex(var->data.aggregate.var, bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16759 return var->data.aggregate.scalar * SCIPvarGetLbAtIndex(var->data.aggregate.var, bdchgidx, after)
16760 + var->data.aggregate.constant;
16761 }
16762 else if( var->data.aggregate.scalar < 0.0 )
16763 {
16764 /* a < 0 -> get upper bound of y */
16765 assert(SCIPvarGetUbAtIndex(var->data.aggregate.var, bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16766 assert(SCIPvarGetUbAtIndex(var->data.aggregate.var, bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16767 return var->data.aggregate.scalar * SCIPvarGetUbAtIndex(var->data.aggregate.var, bdchgidx, after)
16768 + var->data.aggregate.constant;
16769 }
16770 else
16771 {
16772 SCIPerrorMessage("scalar is zero in aggregation\n");
16773 SCIPABORT();
16774 return SCIP_INVALID; /*lint !e527*/
16775 }
16776
16777 case SCIP_VARSTATUS_MULTAGGR:
16778 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
16779 if ( var->data.multaggr.nvars == 1 )
16780 {
16781 assert(var->data.multaggr.vars != NULL);
16782 assert(var->data.multaggr.scalars != NULL);
16783 assert(var->data.multaggr.vars[0] != NULL);
16784
16785 if( var->data.multaggr.scalars[0] > 0.0 )
16786 {
16787 /* a > 0 -> get lower bound of y */
16788 assert(SCIPvarGetLbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16789 assert(SCIPvarGetLbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16790 return var->data.multaggr.scalars[0] * SCIPvarGetLbAtIndex(var->data.multaggr.vars[0], bdchgidx, after)
16791 + var->data.multaggr.constant;
16792 }
16793 else if( var->data.multaggr.scalars[0] < 0.0 )
16794 {
16795 /* a < 0 -> get upper bound of y */
16796 assert(SCIPvarGetUbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16797 assert(SCIPvarGetUbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16798 return var->data.multaggr.scalars[0] * SCIPvarGetUbAtIndex(var->data.multaggr.vars[0], bdchgidx, after)
16799 + var->data.multaggr.constant;
16800 }
16801 else
16802 {
16803 SCIPerrorMessage("scalar is zero in multi-aggregation\n");
16804 SCIPABORT();
16805 return SCIP_INVALID; /*lint !e527*/
16806 }
16807 }
16808 SCIPerrorMessage("cannot get the bounds of a multi-aggregated variable.\n");
16809 SCIPABORT();
16810 return SCIP_INVALID; /*lint !e527*/
16811
16812 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
16813 assert(var->negatedvar != NULL);
16814 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
16815 assert(var->negatedvar->negatedvar == var);
16816 return var->data.negate.constant - SCIPvarGetUbAtIndex(var->negatedvar, bdchgidx, after);
16817 default:
16818 SCIPerrorMessage("unknown variable status\n");
16819 SCIPABORT();
16820 return SCIP_INVALID; /*lint !e527*/
16821 }
16822 }
16823
16824 /** returns upper bound of variable directly before or after the bound change given by the bound change index
16825 * was applied
16826 *
16827 * @deprecated Please use SCIPgetVarUbAtIndex()
16828 */
16829 SCIP_Real SCIPvarGetUbAtIndex(
16830 SCIP_VAR* var, /**< problem variable */
16831 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16832 SCIP_Bool after /**< should the bound change with given index be included? */
16833 )
16834 {
16835 SCIP_VARSTATUS varstatus;
16836 assert(var != NULL);
16837
16838 varstatus = SCIPvarGetStatus(var);
16839
16840 /* get bounds of attached variables */
16841 switch( varstatus )
16842 {
16843 case SCIP_VARSTATUS_ORIGINAL:
16844 assert(var->data.original.transvar != NULL);
16845 return SCIPvarGetUbAtIndex(var->data.original.transvar, bdchgidx, after);
16846
16847 case SCIP_VARSTATUS_COLUMN:
16848 case SCIP_VARSTATUS_LOOSE:
16849 if( bdchgidx == NULL )
16850 return SCIPvarGetUbLocal(var);
16851 else
16852 {
16853 SCIP_BDCHGINFO* bdchginfo;
16854
16855 bdchginfo = SCIPvarGetUbchgInfo(var, bdchgidx, after);
16856 if( bdchginfo != NULL )
16857 return SCIPbdchginfoGetNewbound(bdchginfo);
16858 else
16859 return var->glbdom.ub;
16860 }
16861
16862 case SCIP_VARSTATUS_FIXED:
16863 return var->glbdom.ub;
16864
16865 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
16866 assert(var->data.aggregate.var != NULL);
16867 /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
16868 * corresponding infinity value instead of performing an arithmetical transformation (compare method
16869 * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
16870 * (or is called by) a public interface method; instead, we only assert that values are finite
16871 * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
16872 * positives and negatives if the parameter <numerics/infinity> is modified by the user
16873 */
16874 if( var->data.aggregate.scalar > 0.0 )
16875 {
16876 /* a > 0 -> get lower bound of y */
16877 assert(SCIPvarGetUbAtIndex(var->data.aggregate.var, bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16878 assert(SCIPvarGetUbAtIndex(var->data.aggregate.var, bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16879 return var->data.aggregate.scalar * SCIPvarGetUbAtIndex(var->data.aggregate.var, bdchgidx, after)
16880 + var->data.aggregate.constant;
16881 }
16882 else if( var->data.aggregate.scalar < 0.0 )
16883 {
16884 /* a < 0 -> get upper bound of y */
16885 assert(SCIPvarGetLbAtIndex(var->data.aggregate.var, bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16886 assert(SCIPvarGetLbAtIndex(var->data.aggregate.var, bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16887 return var->data.aggregate.scalar * SCIPvarGetLbAtIndex(var->data.aggregate.var, bdchgidx, after)
16888 + var->data.aggregate.constant;
16889 }
16890 else
16891 {
16892 SCIPerrorMessage("scalar is zero in aggregation\n");
16893 SCIPABORT();
16894 return SCIP_INVALID; /*lint !e527*/
16895 }
16896
16897 case SCIP_VARSTATUS_MULTAGGR:
16898 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
16899 if ( var->data.multaggr.nvars == 1 )
16900 {
16901 assert(var->data.multaggr.vars != NULL);
16902 assert(var->data.multaggr.scalars != NULL);
16903 assert(var->data.multaggr.vars[0] != NULL);
16904
16905 if( var->data.multaggr.scalars[0] > 0.0 )
16906 {
16907 /* a > 0 -> get lower bound of y */
16908 assert(SCIPvarGetUbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16909 assert(SCIPvarGetUbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16910 return var->data.multaggr.scalars[0] * SCIPvarGetUbAtIndex(var->data.multaggr.vars[0], bdchgidx, after)
16911 + var->data.multaggr.constant;
16912 }
16913 else if( var->data.multaggr.scalars[0] < 0.0 )
16914 {
16915 /* a < 0 -> get upper bound of y */
16916 assert(SCIPvarGetLbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16917 assert(SCIPvarGetLbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16918 return var->data.multaggr.scalars[0] * SCIPvarGetLbAtIndex(var->data.multaggr.vars[0], bdchgidx, after)
16919 + var->data.multaggr.constant;
16920 }
16921 else
16922 {
16923 SCIPerrorMessage("scalar is zero in multi-aggregation\n");
16924 SCIPABORT();
16925 return SCIP_INVALID; /*lint !e527*/
16926 }
16927 }
16928 SCIPerrorMessage("cannot get the bounds of a multiple aggregated variable.\n");
16929 SCIPABORT();
16930 return SCIP_INVALID; /*lint !e527*/
16931
16932 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
16933 assert(var->negatedvar != NULL);
16934 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
16935 assert(var->negatedvar->negatedvar == var);
16936 return var->data.negate.constant - SCIPvarGetLbAtIndex(var->negatedvar, bdchgidx, after);
16937
16938 default:
16939 SCIPerrorMessage("unknown variable status\n");
16940 SCIPABORT();
16941 return SCIP_INVALID; /*lint !e527*/
16942 }
16943 }
16944
16945 /** returns lower or upper bound of variable directly before or after the bound change given by the bound change index
16946 * was applied
16947 *
16948 * @deprecated Please use SCIPgetVarBdAtIndex()
16949 */
16950 SCIP_Real SCIPvarGetBdAtIndex(
16951 SCIP_VAR* var, /**< problem variable */
16952 SCIP_BOUNDTYPE boundtype, /**< type of bound: lower or upper bound */
16953 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16954 SCIP_Bool after /**< should the bound change with given index be included? */
16955 )
16956 {
16957 if( boundtype == SCIP_BOUNDTYPE_LOWER )
16958 return SCIPvarGetLbAtIndex(var, bdchgidx, after);
16959 else
16960 {
16961 assert(boundtype == SCIP_BOUNDTYPE_UPPER);
16962 return SCIPvarGetUbAtIndex(var, bdchgidx, after);
16963 }
16964 }
16965
16966 /** returns whether the binary variable was fixed at the time given by the bound change index
16967 *
16968 * @deprecated Please use SCIPgetVarWasFixedAtIndex()
16969 */
16970 SCIP_Bool SCIPvarWasFixedAtIndex(
16971 SCIP_VAR* var, /**< problem variable */
16972 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16973 SCIP_Bool after /**< should the bound change with given index be included? */
16974 )
16975 {
16976 assert(var != NULL);
16977 assert(SCIPvarIsBinary(var));
16978
16979 /* check the current bounds first in order to decide at which bound change information we have to look
16980 * (which is expensive because we have to follow the aggregation tree to the active variable)
16981 */
16982 return ((SCIPvarGetLbLocal(var) > 0.5 && SCIPvarGetLbAtIndex(var, bdchgidx, after) > 0.5)
16983 || (SCIPvarGetUbLocal(var) < 0.5 && SCIPvarGetUbAtIndex(var, bdchgidx, after) < 0.5));
16984 }
16985
16986 /** bound change index representing the initial time before any bound changes took place */
16987 static SCIP_BDCHGIDX initbdchgidx = {-2, 0};
16988
16989 /** bound change index representing the presolving stage */
16990 static SCIP_BDCHGIDX presolvebdchgidx = {-1, 0};
16991
16992 /** returns the last bound change index, at which the bounds of the given variable were tightened */
16993 SCIP_BDCHGIDX* SCIPvarGetLastBdchgIndex(
16994 SCIP_VAR* var /**< problem variable */
16995 )
16996 {
16997 SCIP_BDCHGIDX* lbchgidx;
16998 SCIP_BDCHGIDX* ubchgidx;
16999
17000 assert(var != NULL);
17001
17002 var = SCIPvarGetProbvar(var);
17003
17004 /* check, if variable is original without transformed variable */
17005 if( var == NULL )
17006 return &initbdchgidx;
17007
17008 /* check, if variable was fixed in presolving */
17009 if( !SCIPvarIsActive(var) )
17010 return &presolvebdchgidx;
17011
17012 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
17013
17014 /* get depths of last bound change information for the lower and upper bound */
17015 lbchgidx = (var->nlbchginfos > 0 && !var->lbchginfos[var->nlbchginfos-1].redundant
17016 ? &var->lbchginfos[var->nlbchginfos-1].bdchgidx : &initbdchgidx);
17017 ubchgidx = (var->nubchginfos > 0 && !var->ubchginfos[var->nubchginfos-1].redundant
17018 ? &var->ubchginfos[var->nubchginfos-1].bdchgidx : &initbdchgidx);
17019
17020 if( SCIPbdchgidxIsEarlierNonNull(lbchgidx, ubchgidx) )
17021 return ubchgidx;
17022 else
17023 return lbchgidx;
17024 }
17025
17026 /** returns the last depth level, at which the bounds of the given variable were tightened;
17027 * returns -2, if the variable's bounds are still the global bounds
17028 * returns -1, if the variable was fixed in presolving
17029 */
17030 int SCIPvarGetLastBdchgDepth(
17031 SCIP_VAR* var /**< problem variable */
17032 )
17033 {
17034 SCIP_BDCHGIDX* bdchgidx;
17035
17036 bdchgidx = SCIPvarGetLastBdchgIndex(var);
17037 assert(bdchgidx != NULL);
17038
17039 return bdchgidx->depth;
17040 }
17041
17042 /** returns at which depth in the tree a bound change was applied to the variable that conflicts with the
17043 * given bound; returns -1 if the bound does not conflict with the current local bounds of the variable
17044 */
17045 int SCIPvarGetConflictingBdchgDepth(
17046 SCIP_VAR* var, /**< problem variable */
17047 SCIP_SET* set, /**< global SCIP settings */
17048 SCIP_BOUNDTYPE boundtype, /**< bound type of the conflicting bound */
17049 SCIP_Real bound /**< conflicting bound */
17050 )
17051 {
17052 int i;
17053
17054 assert(var != NULL);
17055 assert(set != NULL);
17056 assert(var->scip == set->scip);
17057
17058 if( boundtype == SCIP_BOUNDTYPE_LOWER )
17059 {
17060 /* check if the bound is in conflict with the current local bounds */
17061 if( SCIPsetIsLE(set, bound, var->locdom.ub) )
17062 return -1;
17063
17064 /* check if the bound is in conflict with the global bound */
17065 if( SCIPsetIsGT(set, bound, var->glbdom.ub) )
17066 return 0;
17067
17068 /* local bounds are in conflict with the given bound -> there must be at least one conflicting change! */
17069 assert(var->nubchginfos > 0);
17070 assert(SCIPsetIsGT(set, bound, var->ubchginfos[var->nubchginfos-1].newbound));
17071
17072 /* search for the first conflicting bound change */
17073 for( i = var->nubchginfos-1; i > 0 && SCIPsetIsGT(set, bound, var->ubchginfos[i-1].newbound); --i )
17074 {
17075 assert(var->ubchginfos[i].var == var); /* perform sanity check on the search for the first conflicting bound */
17076 assert((SCIP_BOUNDTYPE)var->ubchginfos[i].boundtype == SCIP_BOUNDTYPE_UPPER);
17077 }
17078 assert(SCIPsetIsGT(set, bound, var->ubchginfos[i].newbound)); /* bound change i is conflicting */
17079 assert(i == 0 || SCIPsetIsLE(set, bound, var->ubchginfos[i-1].newbound)); /* bound change i-1 is not conflicting */
17080
17081 /* return the depth at which the first conflicting bound change took place */
17082 return var->ubchginfos[i].bdchgidx.depth;
17083 }
17084 else
17085 {
17086 assert(boundtype == SCIP_BOUNDTYPE_UPPER);
17087
17088 /* check if the bound is in conflict with the current local bounds */
17089 if( SCIPsetIsGE(set, bound, var->locdom.lb) )
17090 return -1;
17091
17092 /* check if the bound is in conflict with the global bound */
17093 if( SCIPsetIsLT(set, bound, var->glbdom.lb) )
17094 return 0;
17095
17096 /* local bounds are in conflict with the given bound -> there must be at least one conflicting change! */
17097 assert(var->nlbchginfos > 0);
17098 assert(SCIPsetIsLT(set, bound, var->lbchginfos[var->nlbchginfos-1].newbound));
17099
17100 /* search for the first conflicting bound change */
17101 for( i = var->nlbchginfos-1; i > 0 && SCIPsetIsLT(set, bound, var->lbchginfos[i-1].newbound); --i )
17102 {
17103 assert(var->lbchginfos[i].var == var); /* perform sanity check on the search for the first conflicting bound */
17104 assert((SCIP_BOUNDTYPE)var->lbchginfos[i].boundtype == SCIP_BOUNDTYPE_LOWER);
17105 }
17106 assert(SCIPsetIsLT(set, bound, var->lbchginfos[i].newbound)); /* bound change i is conflicting */
17107 assert(i == 0 || SCIPsetIsGE(set, bound, var->lbchginfos[i-1].newbound)); /* bound change i-1 is not conflicting */
17108
17109 /* return the depth at which the first conflicting bound change took place */
17110 return var->lbchginfos[i].bdchgidx.depth;
17111 }
17112 }
17113
17114 /** returns whether the first binary variable was fixed earlier than the second one;
17115 * returns FALSE, if the first variable is not fixed, and returns TRUE, if the first variable is fixed, but the
17116 * second one is not fixed
17117 */
17118 SCIP_Bool SCIPvarWasFixedEarlier(
17119 SCIP_VAR* var1, /**< first binary variable */
17120 SCIP_VAR* var2 /**< second binary variable */
17121 )
17122 {
17123 SCIP_BDCHGIDX* bdchgidx1;
17124 SCIP_BDCHGIDX* bdchgidx2;
17125
17126 assert(var1 != NULL);
17127 assert(var2 != NULL);
17128 assert(SCIPvarIsBinary(var1));
17129 assert(SCIPvarIsBinary(var2));
17130
17131 var1 = SCIPvarGetProbvar(var1);
17132 var2 = SCIPvarGetProbvar(var2);
17133 assert(var1 != NULL);
17134 assert(var2 != NULL);
17135
17136 /* check, if variables are globally fixed */
17137 if( !SCIPvarIsActive(var2) || var2->glbdom.lb > 0.5 || var2->glbdom.ub < 0.5 )
17138 return FALSE;
17139 if( !SCIPvarIsActive(var1) || var1->glbdom.lb > 0.5 || var1->glbdom.ub < 0.5 )
17140 return TRUE;
17141
17142 assert(SCIPvarGetStatus(var1) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var1) == SCIP_VARSTATUS_COLUMN);
17143 assert(SCIPvarGetStatus(var2) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var2) == SCIP_VARSTATUS_COLUMN);
17144 assert(SCIPvarIsBinary(var1));
17145 assert(SCIPvarIsBinary(var2));
17146 assert(var1->nlbchginfos + var1->nubchginfos <= 1);
17147 assert(var2->nlbchginfos + var2->nubchginfos <= 1);
17148 assert(var1->nlbchginfos == 0 || !var1->lbchginfos[0].redundant); /* otherwise, var would be globally fixed */
17149 assert(var1->nubchginfos == 0 || !var1->ubchginfos[0].redundant); /* otherwise, var would be globally fixed */
17150 assert(var2->nlbchginfos == 0 || !var2->lbchginfos[0].redundant); /* otherwise, var would be globally fixed */
17151 assert(var2->nubchginfos == 0 || !var2->ubchginfos[0].redundant); /* otherwise, var would be globally fixed */
17152
17153 if( var1->nlbchginfos == 1 )
17154 bdchgidx1 = &var1->lbchginfos[0].bdchgidx;
17155 else if( var1->nubchginfos == 1 )
17156 bdchgidx1 = &var1->ubchginfos[0].bdchgidx;
17157 else
17158 bdchgidx1 = NULL;
17159
17160 if( var2->nlbchginfos == 1 )
17161 bdchgidx2 = &var2->lbchginfos[0].bdchgidx;
17162 else if( var2->nubchginfos == 1 )
17163 bdchgidx2 = &var2->ubchginfos[0].bdchgidx;
17164 else
17165 bdchgidx2 = NULL;
17166
17167 return SCIPbdchgidxIsEarlier(bdchgidx1, bdchgidx2);
17168 }
17169
17170
17171
17172 /*
17173 * Hash functions
17174 */
17175
17176 /** gets the key (i.e. the name) of the given variable */
17177 SCIP_DECL_HASHGETKEY(SCIPhashGetKeyVar)
17178 { /*lint --e{715}*/
17179 SCIP_VAR* var = (SCIP_VAR*)elem;
17180
17181 assert(var != NULL);
17182 return var->name;
17183 }
17184
17185
17186
17187
17188 /*
17189 * simple functions implemented as defines
17190 */
17191
17192 /* In debug mode, the following methods are implemented as function calls to ensure
17193 * type validity.
17194 * In optimized mode, the methods are implemented as defines to improve performance.
17195 * However, we want to have them in the library anyways, so we have to undef the defines.
17196 */
17197
17198 #undef SCIPboundchgGetNewbound
17199 #undef SCIPboundchgGetVar
17200 #undef SCIPboundchgGetBoundchgtype
17201 #undef SCIPboundchgGetBoundtype
17202 #undef SCIPboundchgIsRedundant
17203 #undef SCIPdomchgGetNBoundchgs
17204 #undef SCIPdomchgGetBoundchg
17205 #undef SCIPholelistGetLeft
17206 #undef SCIPholelistGetRight
17207 #undef SCIPholelistGetNext
17208 #undef SCIPvarGetName
17209 #undef SCIPvarGetNUses
17210 #undef SCIPvarGetData
17211 #undef SCIPvarSetData
17212 #undef SCIPvarSetDelorigData
17213 #undef SCIPvarSetTransData
17214 #undef SCIPvarSetDeltransData
17215 #undef SCIPvarGetStatus
17216 #undef SCIPvarIsOriginal
17217 #undef SCIPvarIsTransformed
17218 #undef SCIPvarIsNegated
17219 #undef SCIPvarGetType
17220 #undef SCIPvarIsBinary
17221 #undef SCIPvarIsIntegral
17222 #undef SCIPvarIsInitial
17223 #undef SCIPvarIsRemovable
17224 #undef SCIPvarIsDeleted
17225 #undef SCIPvarIsDeletable
17226 #undef SCIPvarMarkDeletable
17227 #undef SCIPvarMarkNotDeletable
17228 #undef SCIPvarIsActive
17229 #undef SCIPvarGetIndex
17230 #undef SCIPvarGetProbindex
17231 #undef SCIPvarGetTransVar
17232 #undef SCIPvarGetCol
17233 #undef SCIPvarIsInLP
17234 #undef SCIPvarGetAggrVar
17235 #undef SCIPvarGetAggrScalar
17236 #undef SCIPvarGetAggrConstant
17237 #undef SCIPvarGetMultaggrNVars
17238 #undef SCIPvarGetMultaggrVars
17239 #undef SCIPvarGetMultaggrScalars
17240 #undef SCIPvarGetMultaggrConstant
17241 #undef SCIPvarGetNegatedVar
17242 #undef SCIPvarGetNegationVar
17243 #undef SCIPvarGetNegationConstant
17244 #undef SCIPvarGetObj
17245 #undef SCIPvarGetLbOriginal
17246 #undef SCIPvarGetUbOriginal
17247 #undef SCIPvarGetHolelistOriginal
17248 #undef SCIPvarGetLbGlobal
17249 #undef SCIPvarGetUbGlobal
17250 #undef SCIPvarGetHolelistGlobal
17251 #undef SCIPvarGetBestBoundGlobal
17252 #undef SCIPvarGetWorstBoundGlobal
17253 #undef SCIPvarGetLbLocal
17254 #undef SCIPvarGetUbLocal
17255 #undef SCIPvarGetHolelistLocal
17256 #undef SCIPvarGetBestBoundLocal
17257 #undef SCIPvarGetWorstBoundLocal
17258 #undef SCIPvarGetBestBoundType
17259 #undef SCIPvarGetWorstBoundType
17260 #undef SCIPvarGetLbLazy
17261 #undef SCIPvarGetUbLazy
17262 #undef SCIPvarGetBranchFactor
17263 #undef SCIPvarGetBranchPriority
17264 #undef SCIPvarGetBranchDirection
17265 #undef SCIPvarGetNVlbs
17266 #undef SCIPvarGetVlbVars
17267 #undef SCIPvarGetVlbCoefs
17268 #undef SCIPvarGetVlbConstants
17269 #undef SCIPvarGetNVubs
17270 #undef SCIPvarGetVubVars
17271 #undef SCIPvarGetVubCoefs
17272 #undef SCIPvarGetVubConstants
17273 #undef SCIPvarGetNImpls
17274 #undef SCIPvarGetImplVars
17275 #undef SCIPvarGetImplTypes
17276 #undef SCIPvarGetImplBounds
17277 #undef SCIPvarGetImplIds
17278 #undef SCIPvarGetNCliques
17279 #undef SCIPvarGetCliques
17280 #undef SCIPvarGetLPSol
17281 #undef SCIPvarGetNLPSol
17282 #undef SCIPvarGetBdchgInfoLb
17283 #undef SCIPvarGetNBdchgInfosLb
17284 #undef SCIPvarGetBdchgInfoUb
17285 #undef SCIPvarGetNBdchgInfosUb
17286 #undef SCIPvarGetValuehistory
17287 #undef SCIPvarGetPseudoSol
17288 #undef SCIPvarCatchEvent
17289 #undef SCIPvarDropEvent
17290 #undef SCIPvarGetVSIDS
17291 #undef SCIPvarGetCliqueComponentIdx
17292 #undef SCIPvarIsRelaxationOnly
17293 #undef SCIPvarMarkRelaxationOnly
17294 #undef SCIPbdchgidxGetPos
17295 #undef SCIPbdchgidxIsEarlierNonNull
17296 #undef SCIPbdchgidxIsEarlier
17297 #undef SCIPbdchginfoGetOldbound
17298 #undef SCIPbdchginfoGetNewbound
17299 #undef SCIPbdchginfoGetVar
17300 #undef SCIPbdchginfoGetChgtype
17301 #undef SCIPbdchginfoGetBoundtype
17302 #undef SCIPbdchginfoGetDepth
17303 #undef SCIPbdchginfoGetPos
17304 #undef SCIPbdchginfoGetIdx
17305 #undef SCIPbdchginfoGetInferVar
17306 #undef SCIPbdchginfoGetInferCons
17307 #undef SCIPbdchginfoGetInferProp
17308 #undef SCIPbdchginfoGetInferInfo
17309 #undef SCIPbdchginfoGetInferBoundtype
17310 #undef SCIPbdchginfoIsRedundant
17311 #undef SCIPbdchginfoHasInferenceReason
17312 #undef SCIPbdchginfoIsTighter
17313
17314
17315 /** returns the new value of the bound in the bound change data */
17316 SCIP_Real SCIPboundchgGetNewbound(
17317 SCIP_BOUNDCHG* boundchg /**< bound change data */
17318 )
17319 {
17320 assert(boundchg != NULL);
17321
17322 return boundchg->newbound;
17323 }
17324
17325 /** returns the variable of the bound change in the bound change data */
17326 SCIP_VAR* SCIPboundchgGetVar(
17327 SCIP_BOUNDCHG* boundchg /**< bound change data */
17328 )
17329 {
17330 assert(boundchg != NULL);
17331
17332 return boundchg->var;
17333 }
17334
17335 /** returns the bound change type of the bound change in the bound change data */
17336 SCIP_BOUNDCHGTYPE SCIPboundchgGetBoundchgtype(
17337 SCIP_BOUNDCHG* boundchg /**< bound change data */
17338 )
17339 {
17340 assert(boundchg != NULL);
17341
17342 return (SCIP_BOUNDCHGTYPE)(boundchg->boundchgtype);
17343 }
17344
17345 /** returns the bound type of the bound change in the bound change data */
17346 SCIP_BOUNDTYPE SCIPboundchgGetBoundtype(
17347 SCIP_BOUNDCHG* boundchg /**< bound change data */
17348 )
17349 {
17350 assert(boundchg != NULL);
17351
17352 return (SCIP_BOUNDTYPE)(boundchg->boundtype);
17353 }
17354
17355 /** returns whether the bound change is redundant due to a more global bound that is at least as strong */
17356 SCIP_Bool SCIPboundchgIsRedundant(
17357 SCIP_BOUNDCHG* boundchg /**< bound change data */
17358 )
17359 {
17360 assert(boundchg != NULL);
17361
17362 return boundchg->redundant;
17363 }
17364
17365 /** returns the number of bound changes in the domain change data */
17366 int SCIPdomchgGetNBoundchgs(
17367 SCIP_DOMCHG* domchg /**< domain change data */
17368 )
17369 {
17370 return domchg != NULL ? domchg->domchgbound.nboundchgs : 0;
17371 }
17372
17373 /** returns a particular bound change in the domain change data */
17374 SCIP_BOUNDCHG* SCIPdomchgGetBoundchg(
17375 SCIP_DOMCHG* domchg, /**< domain change data */
17376 int pos /**< position of the bound change in the domain change data */
17377 )
17378 {
17379 assert(domchg != NULL);
17380 assert(0 <= pos && pos < (int)domchg->domchgbound.nboundchgs);
17381
17382 return &domchg->domchgbound.boundchgs[pos];
17383 }
17384
17385 /** returns left bound of open interval in hole */
17386 SCIP_Real SCIPholelistGetLeft(
17387 SCIP_HOLELIST* holelist /**< hole list pointer to hole of interest */
17388 )
17389 {
17390 assert(holelist != NULL);
17391
17392 return holelist->hole.left;
17393 }
17394
17395 /** returns right bound of open interval in hole */
17396 SCIP_Real SCIPholelistGetRight(
17397 SCIP_HOLELIST* holelist /**< hole list pointer to hole of interest */
17398 )
17399 {
17400 assert(holelist != NULL);
17401
17402 return holelist->hole.right;
17403 }
17404
17405 /** returns next hole in list */
17406 SCIP_HOLELIST* SCIPholelistGetNext(
17407 SCIP_HOLELIST* holelist /**< hole list pointer to hole of interest */
17408 )
17409 {
17410 assert(holelist != NULL);
17411
17412 return holelist->next;
17413 }
17414
17415 /** returns the name of the variable
17416 *
17417 * @note to change the name of a variable, use SCIPchgVarName() from scip.h
17418 */
17419 const char* SCIPvarGetName(
17420 SCIP_VAR* var /**< problem variable */
17421 )
17422 {
17423 assert(var != NULL);
17424
17425 return var->name;
17426 }
17427
17428 /** gets number of times, the variable is currently captured */
17429 int SCIPvarGetNUses(
17430 SCIP_VAR* var /**< problem variable */
17431 )
17432 {
17433 assert(var != NULL);
17434
17435 return var->nuses;
17436 }
17437
17438 /** returns the user data of the variable */
17439 SCIP_VARDATA* SCIPvarGetData(
17440 SCIP_VAR* var /**< problem variable */
17441 )
17442 {
17443 assert(var != NULL);
17444
17445 return var->vardata;
17446 }
17447
17448 /** sets the user data for the variable */
17449 void SCIPvarSetData(
17450 SCIP_VAR* var, /**< problem variable */
17451 SCIP_VARDATA* vardata /**< user variable data */
17452 )
17453 {
17454 assert(var != NULL);
17455
17456 var->vardata = vardata;
17457 }
17458
17459 /** sets method to free user data for the original variable */
17460 void SCIPvarSetDelorigData(
17461 SCIP_VAR* var, /**< problem variable */
17462 SCIP_DECL_VARDELORIG ((*vardelorig)) /**< frees user data of original variable */
17463 )
17464 {
17465 assert(var != NULL);
17466 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL);
17467
17468 var->vardelorig = vardelorig;
17469 }
17470
17471 /** sets method to transform user data of the variable */
17472 void SCIPvarSetTransData(
17473 SCIP_VAR* var, /**< problem variable */
17474 SCIP_DECL_VARTRANS ((*vartrans)) /**< creates transformed user data by transforming original user data */
17475 )
17476 {
17477 assert(var != NULL);
17478 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL);
17479
17480 var->vartrans = vartrans;
17481 }
17482
17483 /** sets method to free transformed user data for the variable */
17484 void SCIPvarSetDeltransData(
17485 SCIP_VAR* var, /**< problem variable */
17486 SCIP_DECL_VARDELTRANS ((*vardeltrans)) /**< frees user data of transformed variable */
17487 )
17488 {
17489 assert(var != NULL);
17490
17491 var->vardeltrans = vardeltrans;
17492 }
17493
17494 /** sets method to copy this variable into sub-SCIPs */
17495 void SCIPvarSetCopyData(
17496 SCIP_VAR* var, /**< problem variable */
17497 SCIP_DECL_VARCOPY ((*varcopy)) /**< copy method of the variable */
17498 )
17499 {
17500 assert(var != NULL);
17501
17502 var->varcopy = varcopy;
17503 }
17504
17505 /** sets the initial flag of a variable; only possible for original or loose variables */
17506 SCIP_RETCODE SCIPvarSetInitial(
17507 SCIP_VAR* var, /**< problem variable */
17508 SCIP_Bool initial /**< initial flag */
17509 )
17510 {
17511 assert(var != NULL);
17512
17513 if( (SCIP_VARSTATUS)var->varstatus != SCIP_VARSTATUS_ORIGINAL && (SCIP_VARSTATUS)var->varstatus != SCIP_VARSTATUS_LOOSE )
17514 return SCIP_INVALIDCALL;
17515
17516 var->initial = initial;
17517
17518 return SCIP_OKAY;
17519 }
17520
17521 /** sets the removable flag of a variable; only possible for original or loose variables */
17522 SCIP_RETCODE SCIPvarSetRemovable(
17523 SCIP_VAR* var, /**< problem variable */
17524 SCIP_Bool removable /**< removable flag */
17525 )
17526 {
17527 assert(var != NULL);
17528
17529 if( (SCIP_VARSTATUS)var->varstatus != SCIP_VARSTATUS_ORIGINAL && (SCIP_VARSTATUS)var->varstatus != SCIP_VARSTATUS_LOOSE )
17530 return SCIP_INVALIDCALL;
17531
17532 var->removable = removable;
17533
17534 return SCIP_OKAY;
17535 }
17536
17537 /** gets status of variable */
17538 SCIP_VARSTATUS SCIPvarGetStatus(
17539 SCIP_VAR* var /**< problem variable */
17540 )
17541 {
17542 assert(var != NULL);
17543
17544 return (SCIP_VARSTATUS)(var->varstatus);
17545 }
17546
17547 /** returns whether the variable belongs to the original problem */
17548 SCIP_Bool SCIPvarIsOriginal(
17549 SCIP_VAR* var /**< problem variable */
17550 )
17551 {
17552 assert(var != NULL);
17553 assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_NEGATED || var->negatedvar != NULL);
17554
17555 return (SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL
17556 || (SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED
17557 && SCIPvarGetStatus(var->negatedvar) == SCIP_VARSTATUS_ORIGINAL));
17558 }
17559
17560 /** returns whether the variable belongs to the transformed problem */
17561 SCIP_Bool SCIPvarIsTransformed(
17562 SCIP_VAR* var /**< problem variable */
17563 )
17564 {
17565 assert(var != NULL);
17566 assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_NEGATED || var->negatedvar != NULL);
17567
17568 return (SCIPvarGetStatus(var) != SCIP_VARSTATUS_ORIGINAL
17569 && (SCIPvarGetStatus(var) != SCIP_VARSTATUS_NEGATED
17570 || SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_ORIGINAL));
17571 }
17572
17573 /** returns whether the variable was created by negation of a different variable */
17574 SCIP_Bool SCIPvarIsNegated(
17575 SCIP_VAR* var /**< problem variable */
17576 )
17577 {
17578 assert(var != NULL);
17579
17580 return (SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
17581 }
17582
17583 /** gets type of variable */
17584 SCIP_VARTYPE SCIPvarGetType(
17585 SCIP_VAR* var /**< problem variable */
17586 )
17587 {
17588 assert(var != NULL);
17589
17590 return (SCIP_VARTYPE)(var->vartype);
17591 }
17592
17593 /** returns TRUE if the variable is of binary type; this is the case if:
17594 * (1) variable type is binary
17595 * (2) variable type is integer or implicit integer and
17596 * (i) the global lower bound is greater than or equal to zero
17597 * (ii) the global upper bound is less than or equal to one
17598 */
17599 SCIP_Bool SCIPvarIsBinary(
17600 SCIP_VAR* var /**< problem variable */
17601 )
17602 {
17603 assert(var != NULL);
17604
17605 return (SCIPvarGetType(var) == SCIP_VARTYPE_BINARY ||
17606 (SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS && var->glbdom.lb >= 0.0 && var->glbdom.ub <= 1.0));
17607 }
17608
17609 /** returns whether variable is of integral type (binary, integer, or implicit integer) */
17610 SCIP_Bool SCIPvarIsIntegral(
17611 SCIP_VAR* var /**< problem variable */
17612 )
17613 {
17614 assert(var != NULL);
17615
17616 return (SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS);
17617 }
17618
17619 /** returns whether variable's column should be present in the initial root LP */
17620 SCIP_Bool SCIPvarIsInitial(
17621 SCIP_VAR* var /**< problem variable */
17622 )
17623 {
17624 assert(var != NULL);
17625
17626 return var->initial;
17627 }
17628
17629 /** returns whether variable's column is removable from the LP (due to aging or cleanup) */
17630 SCIP_Bool SCIPvarIsRemovable(
17631 SCIP_VAR* var /**< problem variable */
17632 )
17633 {
17634 assert(var != NULL);
17635
17636 return var->removable;
17637 }
17638
17639 /** returns whether the variable was deleted from the problem */
17640 SCIP_Bool SCIPvarIsDeleted(
17641 SCIP_VAR* var /**< problem variable */
17642 )
17643 {
17644 assert(var != NULL);
17645
17646 return var->deleted;
17647 }
17648
17649 /** marks the variable to be deletable, i.e., it may be deleted completely from the problem;
17650 * method can only be called before the variable is added to the problem by SCIPaddVar() or SCIPaddPricedVar()
17651 */
17652 void SCIPvarMarkDeletable(
17653 SCIP_VAR* var /**< problem variable */
17654 )
17655 {
17656 assert(var != NULL);
17657 assert(var->probindex == -1);
17658
17659 var->deletable = TRUE;
17660 }
17661
17662 /** marks the variable to be not deletable from the problem */
17663 void SCIPvarMarkNotDeletable(
17664 SCIP_VAR* var
17665 )
17666 {
17667 assert(var != NULL);
17668
17669 var->deletable = FALSE;
17670 }
17671
17672 /** marks variable to be deleted from global structures (cliques etc.) when cleaning up
17673 *
17674 * @note: this is not equivalent to marking the variable itself for deletion, this is done by using SCIPvarMarkDeletable()
17675 */
17676 void SCIPvarMarkDeleteGlobalStructures(
17677 SCIP_VAR* var /**< problem variable */
17678 )
17679 {
17680 assert(var != NULL);
17681
17682 var->delglobalstructs = TRUE;
17683 }
17684
17685 /** returns whether the variable was flagged for deletion from global structures (cliques etc.) */
17686 SCIP_Bool SCIPvarIsMarkedDeleteGlobalStructures(
17687 SCIP_VAR* var /**< problem variable */
17688 )
17689 {
17690 assert(var != NULL);
17691
17692 return var->delglobalstructs;
17693 }
17694
17695 /** returns whether a variable has been introduced to define a relaxation
17696 *
17697 * These variables are only valid for the current SCIP solve round,
17698 * they are not contained in any (checked) constraints, but may be used
17699 * in cutting planes, for example.
17700 * Relaxation-only variables are not copied by SCIPcopyVars and cuts
17701 * that contain these variables are not added as linear constraints when
17702 * restarting or transferring information from a copied SCIP to a SCIP.
17703 * Also conflicts with relaxation-only variables are not generated at
17704 * the moment.
17705 */
17706 SCIP_Bool SCIPvarIsRelaxationOnly(
17707 SCIP_VAR* var /**< problem variable */
17708 )
17709 {
17710 assert(var != NULL);
17711
17712 return var->relaxationonly;
17713 }
17714
17715 /** marks that this variable has only been introduced to define a relaxation
17716 *
17717 * The variable must not have a coefficient in the objective and must be deletable.
17718 * If it is not marked deletable, it will be marked as deletable, which is only possible
17719 * before the variable is added to a problem.
17720 *
17721 * @see SCIPvarIsRelaxationOnly
17722 * @see SCIPvarMarkDeletable
17723 */
17724 void SCIPvarMarkRelaxationOnly(
17725 SCIP_VAR* var /**< problem variable */
17726 )
17727 {
17728 assert(var != NULL);
17729 assert(SCIPvarGetObj(var) == 0.0);
17730
17731 if( !SCIPvarIsDeletable(var) )
17732 SCIPvarMarkDeletable(var);
17733
17734 var->relaxationonly = TRUE;
17735 }
17736
17737 /** returns whether variable is allowed to be deleted completely from the problem */
17738 SCIP_Bool SCIPvarIsDeletable(
17739 SCIP_VAR* var
17740 )
17741 {
17742 assert(var != NULL);
17743
17744 return var->deletable;
17745 }
17746
17747 /** returns whether variable is an active (neither fixed nor aggregated) variable */
17748 SCIP_Bool SCIPvarIsActive(
17749 SCIP_VAR* var /**< problem variable */
17750 )
17751 {
17752 assert(var != NULL);
17753
17754 return (var->probindex >= 0);
17755 }
17756
17757 /** gets unique index of variable */
17758 int SCIPvarGetIndex(
17759 SCIP_VAR* var /**< problem variable */
17760 )
17761 {
17762 assert(var != NULL);
17763
17764 return var->index;
17765 }
17766
17767 /** gets position of variable in problem, or -1 if variable is not active */
17768 int SCIPvarGetProbindex(
17769 SCIP_VAR* var /**< problem variable */
17770 )
17771 {
17772 assert(var != NULL);
17773
17774 return var->probindex;
17775 }
17776
17777 /** gets transformed variable of ORIGINAL variable */
17778 SCIP_VAR* SCIPvarGetTransVar(
17779 SCIP_VAR* var /**< problem variable */
17780 )
17781 {
17782 assert(var != NULL);
17783 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL);
17784
17785 return var->data.original.transvar;
17786 }
17787
17788 /** gets column of COLUMN variable */
17789 SCIP_COL* SCIPvarGetCol(
17790 SCIP_VAR* var /**< problem variable */
17791 )
17792 {
17793 assert(var != NULL);
17794 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
17795
17796 return var->data.col;
17797 }
17798
17799 /** returns whether the variable is a COLUMN variable that is member of the current LP */
17800 SCIP_Bool SCIPvarIsInLP(
17801 SCIP_VAR* var /**< problem variable */
17802 )
17803 {
17804 assert(var != NULL);
17805
17806 return (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN && SCIPcolIsInLP(var->data.col));
17807 }
17808
17809 /** gets aggregation variable y of an aggregated variable x = a*y + c */
17810 SCIP_VAR* SCIPvarGetAggrVar(
17811 SCIP_VAR* var /**< problem variable */
17812 )
17813 {
17814 assert(var != NULL);
17815 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_AGGREGATED);
17816 assert(!var->donotaggr);
17817
17818 return var->data.aggregate.var;
17819 }
17820
17821 /** gets aggregation scalar a of an aggregated variable x = a*y + c */
17822 SCIP_Real SCIPvarGetAggrScalar(
17823 SCIP_VAR* var /**< problem variable */
17824 )
17825 {
17826 assert(var != NULL);
17827 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_AGGREGATED);
17828 assert(!var->donotaggr);
17829
17830 return var->data.aggregate.scalar;
17831 }
17832
17833 /** gets aggregation constant c of an aggregated variable x = a*y + c */
17834 SCIP_Real SCIPvarGetAggrConstant(
17835 SCIP_VAR* var /**< problem variable */
17836 )
17837 {
17838 assert(var != NULL);
17839 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_AGGREGATED);
17840 assert(!var->donotaggr);
17841
17842 return var->data.aggregate.constant;
17843 }
17844
17845 /** gets number n of aggregation variables of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
17846 int SCIPvarGetMultaggrNVars(
17847 SCIP_VAR* var /**< problem variable */
17848 )
17849 {
17850 assert(var != NULL);
17851 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
17852 assert(!var->donotmultaggr);
17853
17854 return var->data.multaggr.nvars;
17855 }
17856
17857 /** gets vector of aggregation variables y of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
17858 SCIP_VAR** SCIPvarGetMultaggrVars(
17859 SCIP_VAR* var /**< problem variable */
17860 )
17861 {
17862 assert(var != NULL);
17863 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
17864 assert(!var->donotmultaggr);
17865
17866 return var->data.multaggr.vars;
17867 }
17868
17869 /** gets vector of aggregation scalars a of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
17870 SCIP_Real* SCIPvarGetMultaggrScalars(
17871 SCIP_VAR* var /**< problem variable */
17872 )
17873 {
17874 assert(var != NULL);
17875 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
17876 assert(!var->donotmultaggr);
17877
17878 return var->data.multaggr.scalars;
17879 }
17880
17881 /** gets aggregation constant c of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
17882 SCIP_Real SCIPvarGetMultaggrConstant(
17883 SCIP_VAR* var /**< problem variable */
17884 )
17885 {
17886 assert(var != NULL);
17887 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
17888 assert(!var->donotmultaggr);
17889
17890 return var->data.multaggr.constant;
17891 }
17892
17893 /** gets the negation of the given variable; may return NULL, if no negation is existing yet */
17894 SCIP_VAR* SCIPvarGetNegatedVar(
17895 SCIP_VAR* var /**< negated problem variable */
17896 )
17897 {
17898 assert(var != NULL);
17899
17900 return var->negatedvar;
17901 }
17902
17903 /** gets the negation variable x of a negated variable x' = offset - x */
17904 SCIP_VAR* SCIPvarGetNegationVar(
17905 SCIP_VAR* var /**< negated problem variable */
17906 )
17907 {
17908 assert(var != NULL);
17909 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
17910
17911 return var->negatedvar;
17912 }
17913
17914 /** gets the negation offset of a negated variable x' = offset - x */
17915 SCIP_Real SCIPvarGetNegationConstant(
17916 SCIP_VAR* var /**< negated problem variable */
17917 )
17918 {
17919 assert(var != NULL);
17920 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
17921
17922 return var->data.negate.constant;
17923 }
17924
17925 /** gets objective function value of variable */
17926 SCIP_Real SCIPvarGetObj(
17927 SCIP_VAR* var /**< problem variable */
17928 )
17929 {
17930 assert(var != NULL);
17931
17932 return var->obj;
17933 }
17934
17935 /** gets the unchanged objective function value of a variable (ignoring temproray changes performed in probing mode) */
17936 SCIP_Real SCIPvarGetUnchangedObj(
17937 SCIP_VAR* var /**< problem variable */
17938 )
17939 {
17940 assert(var != NULL);
17941
17942 return var->unchangedobj;
17943 }
17944
17945 /** gets corresponding objective value of active, fixed, or multi-aggregated problem variable of given variable
17946 * e.g. obj(x) = 1 this method returns for ~x the value -1
17947 */
17948 SCIP_RETCODE SCIPvarGetAggregatedObj(
17949 SCIP_VAR* var, /**< problem variable */
17950 SCIP_Real* aggrobj /**< pointer to store the aggregated objective value */
17951 )
17952 {
17953 SCIP_VAR* probvar = var;
17954 SCIP_Real mult = 1.0;
17955
17956 assert(probvar != NULL);
17957 assert(aggrobj != NULL);
17958
17959 while( probvar != NULL )
17960 {
17961 switch( SCIPvarGetStatus(probvar) )
17962 {
17963 case SCIP_VARSTATUS_ORIGINAL:
17964 case SCIP_VARSTATUS_LOOSE:
17965 case SCIP_VARSTATUS_COLUMN:
17966 (*aggrobj) = mult * SCIPvarGetObj(probvar);
17967 return SCIP_OKAY;
17968
17969 case SCIP_VARSTATUS_FIXED:
17970 assert(SCIPvarGetObj(probvar) == 0.0);
17971 (*aggrobj) = 0.0;
17972 return SCIP_OKAY;
17973
17974 case SCIP_VARSTATUS_MULTAGGR:
17975 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
17976 if ( probvar->data.multaggr.nvars == 1 )
17977 {
17978 assert( probvar->data.multaggr.vars != NULL );
17979 assert( probvar->data.multaggr.scalars != NULL );
17980 assert( probvar->data.multaggr.vars[0] != NULL );
17981 mult *= probvar->data.multaggr.scalars[0];
17982 probvar = probvar->data.multaggr.vars[0];
17983 break;
17984 }
17985 else
17986 {
17987 SCIP_Real tmpobj;
17988 int v;
17989
17990 (*aggrobj) = 0.0;
17991
17992 for( v = probvar->data.multaggr.nvars - 1; v >= 0; --v )
17993 {
17994 SCIP_CALL( SCIPvarGetAggregatedObj(probvar->data.multaggr.vars[v], &tmpobj) );
17995 (*aggrobj) += probvar->data.multaggr.scalars[v] * tmpobj;
17996 }
17997 return SCIP_OKAY;
17998 }
17999
18000 case SCIP_VARSTATUS_AGGREGATED: /* x = a'*x' + c' => a*x + c == (a*a')*x' + (a*c' + c) */
18001 assert(probvar->data.aggregate.var != NULL);
18002 mult *= probvar->data.aggregate.scalar;
18003 probvar = probvar->data.aggregate.var;
18004 break;
18005
18006 case SCIP_VARSTATUS_NEGATED: /* x = - x' + c' => a*x + c == (-a)*x' + (a*c' + c) */
18007 assert(probvar->negatedvar != NULL);
18008 assert(SCIPvarGetStatus(probvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
18009 assert(probvar->negatedvar->negatedvar == probvar);
18010 mult *= -1.0;
18011 probvar = probvar->negatedvar;
18012 break;
18013
18014 default:
18015 SCIPABORT();
18016 return SCIP_INVALIDDATA; /*lint !e527*/
18017 }
18018 }
18019
18020 return SCIP_INVALIDDATA;
18021 }
18022
18023 /** gets original lower bound of original problem variable (i.e. the bound set in problem creation) */
18024 SCIP_Real SCIPvarGetLbOriginal(
18025 SCIP_VAR* var /**< original problem variable */
18026 )
18027 {
18028 assert(var != NULL);
18029 assert(SCIPvarIsOriginal(var));
18030
18031 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
18032 return var->data.original.origdom.lb;
18033 else
18034 {
18035 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
18036 assert(var->negatedvar != NULL);
18037 assert(SCIPvarGetStatus(var->negatedvar) == SCIP_VARSTATUS_ORIGINAL);
18038
18039 return var->data.negate.constant - var->negatedvar->data.original.origdom.ub;
18040 }
18041 }
18042
18043 /** gets original upper bound of original problem variable (i.e. the bound set in problem creation) */
18044 SCIP_Real SCIPvarGetUbOriginal(
18045 SCIP_VAR* var /**< original problem variable */
18046 )
18047 {
18048 assert(var != NULL);
18049 assert(SCIPvarIsOriginal(var));
18050
18051 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
18052 return var->data.original.origdom.ub;
18053 else
18054 {
18055 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
18056 assert(var->negatedvar != NULL);
18057 assert(SCIPvarGetStatus(var->negatedvar) == SCIP_VARSTATUS_ORIGINAL);
18058
18059 return var->data.negate.constant - var->negatedvar->data.original.origdom.lb;
18060 }
18061 }
18062
18063 /** gets the original hole list of an original variable */
18064 SCIP_HOLELIST* SCIPvarGetHolelistOriginal(
18065 SCIP_VAR* var /**< problem variable */
18066 )
18067 {
18068 assert(var != NULL);
18069 assert(SCIPvarIsOriginal(var));
18070
18071 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
18072 return var->data.original.origdom.holelist;
18073
18074 return NULL;
18075 }
18076
18077 /** gets global lower bound of variable */
18078 SCIP_Real SCIPvarGetLbGlobal(
18079 SCIP_VAR* var /**< problem variable */
18080 )
18081 {
18082 assert(var != NULL);
18083
18084 return var->glbdom.lb;
18085 }
18086
18087 /** gets global upper bound of variable */
18088 SCIP_Real SCIPvarGetUbGlobal(
18089 SCIP_VAR* var /**< problem variable */
18090 )
18091 {
18092 assert(var != NULL);
18093
18094 return var->glbdom.ub;
18095 }
18096
18097 /** gets the global hole list of an active variable */
18098 SCIP_HOLELIST* SCIPvarGetHolelistGlobal(
18099 SCIP_VAR* var /**< problem variable */
18100 )
18101 {
18102 assert(var != NULL);
18103
18104 return var->glbdom.holelist;
18105 }
18106
18107 /** gets best global bound of variable with respect to the objective function */
18108 SCIP_Real SCIPvarGetBestBoundGlobal(
18109 SCIP_VAR* var /**< problem variable */
18110 )
18111 {
18112 assert(var != NULL);
18113
18114 if( var->obj >= 0.0 )
18115 return var->glbdom.lb;
18116 else
18117 return var->glbdom.ub;
18118 }
18119
18120 /** gets worst global bound of variable with respect to the objective function */
18121 SCIP_Real SCIPvarGetWorstBoundGlobal(
18122 SCIP_VAR* var /**< problem variable */
18123 )
18124 {
18125 assert(var != NULL);
18126
18127 if( var->obj >= 0.0 )
18128 return var->glbdom.ub;
18129 else
18130 return var->glbdom.lb;
18131 }
18132
18133 /** gets current lower bound of variable */
18134 SCIP_Real SCIPvarGetLbLocal(
18135 SCIP_VAR* var /**< problem variable */
18136 )
18137 {
18138 assert(var != NULL);
18139
18140 return var->locdom.lb;
18141 }
18142
18143 /** gets current upper bound of variable */
18144 SCIP_Real SCIPvarGetUbLocal(
18145 SCIP_VAR* var /**< problem variable */
18146 )
18147 {
18148 assert(var != NULL);
18149
18150 return var->locdom.ub;
18151 }
18152
18153 /** gets the current hole list of an active variable */
18154 SCIP_HOLELIST* SCIPvarGetHolelistLocal(
18155 SCIP_VAR* var /**< problem variable */
18156 )
18157 {
18158 assert(var != NULL);
18159
18160 return var->locdom.holelist;
18161 }
18162
18163 /** gets best local bound of variable with respect to the objective function */
18164 SCIP_Real SCIPvarGetBestBoundLocal(
18165 SCIP_VAR* var /**< problem variable */
18166 )
18167 {
18168 assert(var != NULL);
18169
18170 if( var->obj >= 0.0 )
18171 return var->locdom.lb;
18172 else
18173 return var->locdom.ub;
18174 }
18175
18176 /** gets worst local bound of variable with respect to the objective function */
18177 SCIP_Real SCIPvarGetWorstBoundLocal(
18178 SCIP_VAR* var /**< problem variable */
18179 )
18180 {
18181 assert(var != NULL);
18182
18183 if( var->obj >= 0.0 )
18184 return var->locdom.ub;
18185 else
18186 return var->locdom.lb;
18187 }
18188
18189 /** gets type (lower or upper) of best bound of variable with respect to the objective function */
18190 SCIP_BOUNDTYPE SCIPvarGetBestBoundType(
18191 SCIP_VAR* var /**< problem variable */
18192 )
18193 {
18194 assert(var != NULL);
18195
18196 if( var->obj >= 0.0 )
18197 return SCIP_BOUNDTYPE_LOWER;
18198 else
18199 return SCIP_BOUNDTYPE_UPPER;
18200 }
18201
18202 /** gets type (lower or upper) of worst bound of variable with respect to the objective function */
18203 SCIP_BOUNDTYPE SCIPvarGetWorstBoundType(
18204 SCIP_VAR* var /**< problem variable */
18205 )
18206 {
18207 assert(var != NULL);
18208
18209 if( var->obj >= 0.0 )
18210 return SCIP_BOUNDTYPE_UPPER;
18211 else
18212 return SCIP_BOUNDTYPE_LOWER;
18213 }
18214
18215 /** gets lazy lower bound of variable, returns -infinity if the variable has no lazy lower bound */
18216 SCIP_Real SCIPvarGetLbLazy(
18217 SCIP_VAR* var /**< problem variable */
18218 )
18219 {
18220 assert(var != NULL);
18221
18222 return var->lazylb;
18223 }
18224
18225 /** gets lazy upper bound of variable, returns infinity if the variable has no lazy upper bound */
18226 SCIP_Real SCIPvarGetUbLazy(
18227 SCIP_VAR* var /**< problem variable */
18228 )
18229 {
18230 assert(var != NULL);
18231
18232 return var->lazyub;
18233 }
18234
18235 /** gets the branch factor of the variable; this value can be used in the branching methods to scale the score
18236 * values of the variables; higher factor leads to a higher probability that this variable is chosen for branching
18237 */
18238 SCIP_Real SCIPvarGetBranchFactor(
18239 SCIP_VAR* var /**< problem variable */
18240 )
18241 {
18242 assert(var != NULL);
18243
18244 return var->branchfactor;
18245 }
18246
18247 /** gets the branch priority of the variable; variables with higher priority should always be preferred to variables
18248 * with lower priority
18249 */
18250 int SCIPvarGetBranchPriority(
18251 SCIP_VAR* var /**< problem variable */
18252 )
18253 {
18254 assert(var != NULL);
18255
18256 return var->branchpriority;
18257 }
18258
18259 /** gets the preferred branch direction of the variable (downwards, upwards, or auto) */
18260 SCIP_BRANCHDIR SCIPvarGetBranchDirection(
18261 SCIP_VAR* var /**< problem variable */
18262 )
18263 {
18264 assert(var != NULL);
18265
18266 return (SCIP_BRANCHDIR)var->branchdirection;
18267 }
18268
18269 /** gets number of variable lower bounds x >= b_i*z_i + d_i of given variable x */
18270 int SCIPvarGetNVlbs(
18271 SCIP_VAR* var /**< problem variable */
18272 )
18273 {
18274 assert(var != NULL);
18275
18276 return SCIPvboundsGetNVbds(var->vlbs);
18277 }
18278
18279 /** gets array with bounding variables z_i in variable lower bounds x >= b_i*z_i + d_i of given variable x;
18280 * the variable bounds are sorted by increasing variable index of the bounding variable z_i (see SCIPvarGetIndex())
18281 */
18282 SCIP_VAR** SCIPvarGetVlbVars(
18283 SCIP_VAR* var /**< problem variable */
18284 )
18285 {
18286 assert(var != NULL);
18287
18288 return SCIPvboundsGetVars(var->vlbs);
18289 }
18290
18291 /** gets array with bounding coefficients b_i in variable lower bounds x >= b_i*z_i + d_i of given variable x */
18292 SCIP_Real* SCIPvarGetVlbCoefs(
18293 SCIP_VAR* var /**< problem variable */
18294 )
18295 {
18296 assert(var != NULL);
18297
18298 return SCIPvboundsGetCoefs(var->vlbs);
18299 }
18300
18301 /** gets array with bounding constants d_i in variable lower bounds x >= b_i*z_i + d_i of given variable x */
18302 SCIP_Real* SCIPvarGetVlbConstants(
18303 SCIP_VAR* var /**< problem variable */
18304 )
18305 {
18306 assert(var != NULL);
18307
18308 return SCIPvboundsGetConstants(var->vlbs);
18309 }
18310
18311 /** gets number of variable upper bounds x <= b_i*z_i + d_i of given variable x */
18312 int SCIPvarGetNVubs(
18313 SCIP_VAR* var /**< problem variable */
18314 )
18315 {
18316 assert(var != NULL);
18317
18318 return SCIPvboundsGetNVbds(var->vubs);
18319 }
18320
18321 /** gets array with bounding variables z_i in variable upper bounds x <= b_i*z_i + d_i of given variable x;
18322 * the variable bounds are sorted by increasing variable index of the bounding variable z_i (see SCIPvarGetIndex())
18323 */
18324 SCIP_VAR** SCIPvarGetVubVars(
18325 SCIP_VAR* var /**< problem variable */
18326 )
18327 {
18328 assert(var != NULL);
18329
18330 return SCIPvboundsGetVars(var->vubs);
18331 }
18332
18333 /** gets array with bounding coefficients b_i in variable upper bounds x <= b_i*z_i + d_i of given variable x */
18334 SCIP_Real* SCIPvarGetVubCoefs(
18335 SCIP_VAR* var /**< problem variable */
18336 )
18337 {
18338 assert(var != NULL);
18339
18340 return SCIPvboundsGetCoefs(var->vubs);
18341 }
18342
18343 /** gets array with bounding constants d_i in variable upper bounds x <= b_i*z_i + d_i of given variable x */
18344 SCIP_Real* SCIPvarGetVubConstants(
18345 SCIP_VAR* var /**< problem variable */
18346 )
18347 {
18348 assert(var != NULL);
18349
18350 return SCIPvboundsGetConstants(var->vubs);
18351 }
18352
18353 /** gets number of implications y <= b or y >= b for x == 0 or x == 1 of given active problem variable x,
18354 * there are no implications for nonbinary variable x
18355 */
18356 int SCIPvarGetNImpls(
18357 SCIP_VAR* var, /**< active problem variable */
18358 SCIP_Bool varfixing /**< FALSE for implications for x == 0, TRUE for x == 1 */
18359 )
18360 {
18361 assert(var != NULL);
18362 assert(SCIPvarIsActive(var));
18363
18364 return SCIPimplicsGetNImpls(var->implics, varfixing);
18365 }
18366
18367 /** gets array with implication variables y of implications y <= b or y >= b for x == 0 or x == 1 of given active
18368 * problem variable x, there are no implications for nonbinary variable x;
18369 * the implications are sorted such that implications with binary implied variables precede the ones with non-binary
18370 * implied variables, and as a second criteria, the implied variables are sorted by increasing variable index
18371 * (see SCIPvarGetIndex())
18372 */
18373 SCIP_VAR** SCIPvarGetImplVars(
18374 SCIP_VAR* var, /**< active problem variable */
18375 SCIP_Bool varfixing /**< FALSE for implications for x == 0, TRUE for x == 1 */
18376 )
18377 {
18378 assert(var != NULL);
18379 assert(SCIPvarIsActive(var));
18380
18381 return SCIPimplicsGetVars(var->implics, varfixing);
18382 }
18383
18384 /** gets array with implication types of implications y <= b or y >= b for x == 0 or x == 1 of given active problem
18385 * variable x (SCIP_BOUNDTYPE_UPPER if y <= b, SCIP_BOUNDTYPE_LOWER if y >= b),
18386 * there are no implications for nonbinary variable x
18387 */
18388 SCIP_BOUNDTYPE* SCIPvarGetImplTypes(
18389 SCIP_VAR* var, /**< active problem variable */
18390 SCIP_Bool varfixing /**< FALSE for implications for x == 0, TRUE for x == 1 */
18391 )
18392 {
18393 assert(var != NULL);
18394 assert(SCIPvarIsActive(var));
18395
18396 return SCIPimplicsGetTypes(var->implics, varfixing);
18397 }
18398
18399 /** gets array with implication bounds b of implications y <= b or y >= b for x == 0 or x == 1 of given active problem
18400 * variable x, there are no implications for nonbinary variable x
18401 */
18402 SCIP_Real* SCIPvarGetImplBounds(
18403 SCIP_VAR* var, /**< active problem variable */
18404 SCIP_Bool varfixing /**< FALSE for implications for x == 0, TRUE for x == 1 */
18405 )
18406 {
18407 assert(var != NULL);
18408 assert(SCIPvarIsActive(var));
18409
18410 return SCIPimplicsGetBounds(var->implics, varfixing);
18411 }
18412
18413 /** Gets array with unique ids of implications y <= b or y >= b for x == 0 or x == 1 of given active problem variable x,
18414 * there are no implications for nonbinary variable x.
18415 * If an implication is a shortcut, i.e., it was added as part of the transitive closure of another implication,
18416 * its id is negative, otherwise it is nonnegative.
18417 */
18418 int* SCIPvarGetImplIds(
18419 SCIP_VAR* var, /**< active problem variable */
18420 SCIP_Bool varfixing /**< FALSE for implications for x == 0, TRUE for x == 1 */
18421 )
18422 {
18423 assert(var != NULL);
18424 assert(SCIPvarIsActive(var));
18425
18426 return SCIPimplicsGetIds(var->implics, varfixing);
18427 }
18428
18429 /** gets number of cliques, the active variable is contained in */
18430 int SCIPvarGetNCliques(
18431 SCIP_VAR* var, /**< active problem variable */
18432 SCIP_Bool varfixing /**< FALSE for cliques containing x == 0, TRUE for x == 1 */
18433 )
18434 {
18435 assert(var != NULL);
18436
18437 return SCIPcliquelistGetNCliques(var->cliquelist, varfixing);
18438 }
18439
18440 /** gets array of cliques, the active variable is contained in */
18441 SCIP_CLIQUE** SCIPvarGetCliques(
18442 SCIP_VAR* var, /**< active problem variable */
18443 SCIP_Bool varfixing /**< FALSE for cliques containing x == 0, TRUE for x == 1 */
18444 )
18445 {
18446 assert(var != NULL);
18447
18448 return SCIPcliquelistGetCliques(var->cliquelist, varfixing);
18449 }
18450
18451 /** gets primal LP solution value of variable */
18452 SCIP_Real SCIPvarGetLPSol(
18453 SCIP_VAR* var /**< problem variable */
18454 )
18455 {
18456 assert(var != NULL);
18457
18458 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
18459 return SCIPcolGetPrimsol(var->data.col);
18460 else
18461 return SCIPvarGetLPSol_rec(var);
18462 }
18463
18464 /** gets primal NLP solution value of variable */
18465 SCIP_Real SCIPvarGetNLPSol(
18466 SCIP_VAR* var /**< problem variable */
18467 )
18468 {
18469 assert(var != NULL);
18470
18471 if( (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE) )
18472 return var->nlpsol;
18473 else
18474 return SCIPvarGetNLPSol_rec(var);
18475 }
18476
18477 /** return lower bound change info at requested position */
18478 SCIP_BDCHGINFO* SCIPvarGetBdchgInfoLb(
18479 SCIP_VAR* var, /**< problem variable */
18480 int pos /**< requested position */
18481 )
18482 {
18483 assert(pos >= 0);
18484 assert(pos < var->nlbchginfos);
18485
18486 return &var->lbchginfos[pos];
18487 }
18488
18489 /** gets the number of lower bound change info array */
18490 int SCIPvarGetNBdchgInfosLb(
18491 SCIP_VAR* var /**< problem variable */
18492 )
18493 {
18494 return var->nlbchginfos;
18495 }
18496
18497 /** return upper bound change info at requested position */
18498 SCIP_BDCHGINFO* SCIPvarGetBdchgInfoUb(
18499 SCIP_VAR* var, /**< problem variable */
18500 int pos /**< requested position */
18501 )
18502 {
18503 assert(pos >= 0);
18504 assert(pos < var->nubchginfos);
18505
18506 return &var->ubchginfos[pos];
18507 }
18508
18509 /** gets the number upper bound change info array */
18510 int SCIPvarGetNBdchgInfosUb(
18511 SCIP_VAR* var /**< problem variable */
18512 )
18513 {
18514 assert(var != NULL);
18515
18516 return var->nubchginfos;
18517 }
18518
18519 /** returns the value based history for the variable */
18520 SCIP_VALUEHISTORY* SCIPvarGetValuehistory(
18521 SCIP_VAR* var /**< problem variable */
18522 )
18523 {
18524 assert(var != NULL);
18525
18526 return var->valuehistory;
18527 }
18528
18529 /** gets pseudo solution value of variable */
18530 SCIP_Real SCIPvarGetPseudoSol(
18531 SCIP_VAR* var /**< problem variable */
18532 )
18533 {
18534 assert(var != NULL);
18535
18536 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
18537 return SCIPvarGetBestBoundLocal(var);
18538 else
18539 return SCIPvarGetPseudoSol_rec(var);
18540 }
18541
18542 /** returns the variable's VSIDS score */
18543 SCIP_Real SCIPvarGetVSIDS(
18544 SCIP_VAR* var, /**< problem variable */
18545 SCIP_STAT* stat, /**< problem statistics */
18546 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
18547 )
18548 {
18549 assert(var != NULL);
18550
18551 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
18552 return SCIPhistoryGetVSIDS(var->history, dir)/stat->vsidsweight;
18553 else
18554 return SCIPvarGetVSIDS_rec(var, stat, dir);
18555 }
18556
18557 /** includes event handler with given data in variable's event filter */
18558 SCIP_RETCODE SCIPvarCatchEvent(
18559 SCIP_VAR* var, /**< problem variable */
18560 BMS_BLKMEM* blkmem, /**< block memory */
18561 SCIP_SET* set, /**< global SCIP settings */
18562 SCIP_EVENTTYPE eventtype, /**< event type to catch */
18563 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
18564 SCIP_EVENTDATA* eventdata, /**< event data to pass to the event handler for the event processing */
18565 int* filterpos /**< pointer to store position of event filter entry, or NULL */
18566 )
18567 {
18568 assert(var != NULL);
18569 assert(set != NULL);
18570 assert(var->scip == set->scip);
18571 assert(var->eventfilter != NULL);
18572 assert((eventtype & ~SCIP_EVENTTYPE_VARCHANGED) == 0);
18573 assert((eventtype & SCIP_EVENTTYPE_VARCHANGED) != 0);
18574 assert(SCIPvarIsTransformed(var));
18575
18576 SCIPsetDebugMsg(set, "catch event of type 0x%" SCIP_EVENTTYPE_FORMAT " of variable <%s> with handler %p and data %p\n",
18577 eventtype, var->name, (void*)eventhdlr, (void*)eventdata);
18578
18579 SCIP_CALL( SCIPeventfilterAdd(var->eventfilter, blkmem, set, eventtype, eventhdlr, eventdata, filterpos) );
18580
18581 return SCIP_OKAY;
18582 }
18583
18584 /** deletes event handler with given data from variable's event filter */
18585 SCIP_RETCODE SCIPvarDropEvent(
18586 SCIP_VAR* var, /**< problem variable */
18587 BMS_BLKMEM* blkmem, /**< block memory */
18588 SCIP_SET* set, /**< global SCIP settings */
18589 SCIP_EVENTTYPE eventtype, /**< event type mask of dropped event */
18590 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
18591 SCIP_EVENTDATA* eventdata, /**< event data to pass to the event handler for the event processing */
18592 int filterpos /**< position of event filter entry returned by SCIPvarCatchEvent(), or -1 */
18593 )
18594 {
18595 assert(var != NULL);
18596 assert(set != NULL);
18597 assert(var->scip == set->scip);
18598 assert(var->eventfilter != NULL);
18599 assert(SCIPvarIsTransformed(var));
18600
18601 SCIPsetDebugMsg(set, "drop event of variable <%s> with handler %p and data %p\n", var->name, (void*)eventhdlr,
18602 (void*)eventdata);
18603
18604 SCIP_CALL( SCIPeventfilterDel(var->eventfilter, blkmem, set, eventtype, eventhdlr, eventdata, filterpos) );
18605
18606 return SCIP_OKAY;
18607 }
18608
18609 /** returns the position of the bound change index */
18610 int SCIPbdchgidxGetPos(
18611 SCIP_BDCHGIDX* bdchgidx /**< bound change index */
18612 )
18613 {
18614 assert(bdchgidx != NULL);
18615
18616 return bdchgidx->pos;
18617 }
18618
18619 /** returns whether first bound change index belongs to an earlier applied bound change than second one */
18620 SCIP_Bool SCIPbdchgidxIsEarlierNonNull(
18621 SCIP_BDCHGIDX* bdchgidx1, /**< first bound change index */
18622 SCIP_BDCHGIDX* bdchgidx2 /**< second bound change index */
18623 )
18624 {
18625 assert(bdchgidx1 != NULL);
18626 assert(bdchgidx1->depth >= -2);
18627 assert(bdchgidx1->pos >= 0);
18628 assert(bdchgidx2 != NULL);
18629 assert(bdchgidx2->depth >= -2);
18630 assert(bdchgidx2->pos >= 0);
18631
18632 return (bdchgidx1->depth < bdchgidx2->depth)
18633 || (bdchgidx1->depth == bdchgidx2->depth && (bdchgidx1->pos < bdchgidx2->pos));
18634 }
18635
18636 /** returns whether first bound change index belongs to an earlier applied bound change than second one;
18637 * if a bound change index is NULL, the bound change index represents the current time, i.e. the time after the
18638 * last bound change was applied to the current node
18639 */
18640 SCIP_Bool SCIPbdchgidxIsEarlier(
18641 SCIP_BDCHGIDX* bdchgidx1, /**< first bound change index, or NULL */
18642 SCIP_BDCHGIDX* bdchgidx2 /**< second bound change index, or NULL */
18643 )
18644 {
18645 assert(bdchgidx1 == NULL || bdchgidx1->depth >= -2);
18646 assert(bdchgidx1 == NULL || bdchgidx1->pos >= 0);
18647 assert(bdchgidx2 == NULL || bdchgidx2->depth >= -2);
18648 assert(bdchgidx2 == NULL || bdchgidx2->pos >= 0);
18649
18650 if( bdchgidx1 == NULL )
18651 return FALSE;
18652 else if( bdchgidx2 == NULL )
18653 return TRUE;
18654 else
18655 return (bdchgidx1->depth < bdchgidx2->depth)
18656 || (bdchgidx1->depth == bdchgidx2->depth && (bdchgidx1->pos < bdchgidx2->pos));
18657 }
18658
18659 /** returns old bound that was overwritten for given bound change information */
18660 SCIP_Real SCIPbdchginfoGetOldbound(
18661 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18662 )
18663 {
18664 assert(bdchginfo != NULL);
18665
18666 return bdchginfo->oldbound;
18667 }
18668
18669 /** returns new bound installed for given bound change information */
18670 SCIP_Real SCIPbdchginfoGetNewbound(
18671 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18672 )
18673 {
18674 assert(bdchginfo != NULL);
18675
18676 return bdchginfo->newbound;
18677 }
18678
18679 /** returns variable that belongs to the given bound change information */
18680 SCIP_VAR* SCIPbdchginfoGetVar(
18681 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18682 )
18683 {
18684 assert(bdchginfo != NULL);
18685
18686 return bdchginfo->var;
18687 }
18688
18689 /** returns whether the bound change information belongs to a branching decision or a deduction */
18690 SCIP_BOUNDCHGTYPE SCIPbdchginfoGetChgtype(
18691 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18692 )
18693 {
18694 assert(bdchginfo != NULL);
18695
18696 return (SCIP_BOUNDCHGTYPE)(bdchginfo->boundchgtype);
18697 }
18698
18699 /** returns whether the bound change information belongs to a lower or upper bound change */
18700 SCIP_BOUNDTYPE SCIPbdchginfoGetBoundtype(
18701 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18702 )
18703 {
18704 assert(bdchginfo != NULL);
18705
18706 return (SCIP_BOUNDTYPE)(bdchginfo->boundtype);
18707 }
18708
18709 /** returns depth level of given bound change information */
18710 int SCIPbdchginfoGetDepth(
18711 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18712 )
18713 {
18714 assert(bdchginfo != NULL);
18715
18716 return bdchginfo->bdchgidx.depth;
18717 }
18718
18719 /** returns bound change position in its depth level of given bound change information */
18720 int SCIPbdchginfoGetPos(
18721 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18722 )
18723 {
18724 assert(bdchginfo != NULL);
18725
18726 return bdchginfo->bdchgidx.pos;
18727 }
18728
18729 /** returns bound change index of given bound change information */
18730 SCIP_BDCHGIDX* SCIPbdchginfoGetIdx(
18731 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18732 )
18733 {
18734 assert(bdchginfo != NULL);
18735
18736 return &bdchginfo->bdchgidx;
18737 }
18738
18739 /** returns inference variable of given bound change information */
18740 SCIP_VAR* SCIPbdchginfoGetInferVar(
18741 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18742 )
18743 {
18744 assert(bdchginfo != NULL);
18745 assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER
18746 || (SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER);
18747
18748 return bdchginfo->inferencedata.var;
18749 }
18750
18751 /** returns inference constraint of given bound change information */
18752 SCIP_CONS* SCIPbdchginfoGetInferCons(
18753 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18754 )
18755 {
18756 assert(bdchginfo != NULL);
18757 assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER);
18758 assert(bdchginfo->inferencedata.reason.cons != NULL);
18759
18760 return bdchginfo->inferencedata.reason.cons;
18761 }
18762
18763 /** returns inference propagator of given bound change information, or NULL if no propagator was responsible */
18764 SCIP_PROP* SCIPbdchginfoGetInferProp(
18765 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18766 )
18767 {
18768 assert(bdchginfo != NULL);
18769 assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER);
18770
18771 return bdchginfo->inferencedata.reason.prop;
18772 }
18773
18774 /** returns inference user information of given bound change information */
18775 int SCIPbdchginfoGetInferInfo(
18776 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18777 )
18778 {
18779 assert(bdchginfo != NULL);
18780 assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER
18781 || (SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER);
18782
18783 return bdchginfo->inferencedata.info;
18784 }
18785
18786 /** returns inference bound of inference variable of given bound change information */
18787 SCIP_BOUNDTYPE SCIPbdchginfoGetInferBoundtype(
18788 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18789 )
18790 {
18791 assert(bdchginfo != NULL);
18792 assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER
18793 || (SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER);
18794
18795 return (SCIP_BOUNDTYPE)(bdchginfo->inferboundtype);
18796 }
18797
18798 /** returns the relaxed bound change type */
18799 SCIP_Real SCIPbdchginfoGetRelaxedBound(
18800 SCIP_BDCHGINFO* bdchginfo /**< bound change to add to the conflict set */
18801 )
18802 {
18803 return ((SCIP_BOUNDTYPE)(bdchginfo->boundtype) == SCIP_BOUNDTYPE_LOWER ? bdchginfo->var->conflictrelaxedlb : bdchginfo->var->conflictrelaxedub);
18804 }
18805
18806
18807 /** returns whether the bound change information belongs to a redundant bound change */
18808 SCIP_Bool SCIPbdchginfoIsRedundant(
18809 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18810 )
18811 {
18812 assert(bdchginfo != NULL);
18813 assert(bdchginfo->redundant == (bdchginfo->oldbound == bdchginfo->newbound)); /*lint !e777*/
18814
18815 return bdchginfo->redundant;
18816 }
18817
18818 /** returns whether the bound change has an inference reason (constraint or propagator), that can be resolved */
18819 SCIP_Bool SCIPbdchginfoHasInferenceReason(
18820 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18821 )
18822 {
18823 assert(bdchginfo != NULL);
18824
18825 return ((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER)
18826 || ((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER
18827 && bdchginfo->inferencedata.reason.prop != NULL);
18828 }
18829
18830 /** for two bound change informations belonging to the same variable and bound, returns whether the first bound change
18831 * has a tighter new bound as the second bound change
18832 */
18833 SCIP_Bool SCIPbdchginfoIsTighter(
18834 SCIP_BDCHGINFO* bdchginfo1, /**< first bound change information */
18835 SCIP_BDCHGINFO* bdchginfo2 /**< second bound change information */
18836 )
18837 {
18838 assert(bdchginfo1 != NULL);
18839 assert(bdchginfo2 != NULL);
18840 assert(bdchginfo1->var == bdchginfo2->var);
18841 assert(bdchginfo1->boundtype == bdchginfo2->boundtype);
18842
18843 return (SCIPbdchginfoGetBoundtype(bdchginfo1) == SCIP_BOUNDTYPE_LOWER
18844 ? bdchginfo1->newbound > bdchginfo2->newbound
18845 : bdchginfo1->newbound < bdchginfo2->newbound);
18846 }
18847